From aab5b7406bd3addad2afcbad362bf3ca67729f3f Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Mon, 18 Apr 2022 22:59:36 +0530 Subject: [PATCH 01/21] Create Pattern component to represent different fill types --- src/Lib/Components/Pattern.vala | 108 ++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 src/Lib/Components/Pattern.vala diff --git a/src/Lib/Components/Pattern.vala b/src/Lib/Components/Pattern.vala new file mode 100644 index 00000000..f38c9d8d --- /dev/null +++ b/src/Lib/Components/Pattern.vala @@ -0,0 +1,108 @@ + +/** + * Copyright (c) 2022 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: Ashish Shevale + */ + +public class Akira.Lib.Components.Pattern { + public enum PatternType { + SOLID, + LINEAR, + RADIAL, + } + + public struct StopColor { + // Value between 0 and 1. Represents what distance the stop color is located at. + public double offset; + public Gdk.RGBA color; + } + + public PatternType type; + public Gee.TreeSet colors; + public bool hidden; + + public Pattern.solid (Gdk.RGBA color, bool hidden) { + this.type = PatternType.SOLID; + this.hidden = hidden; + + colors = new Gee.TreeSet (are_equal); + colors.add (StopColor () {offset = 0, color = color}); + } + + public Pattern.linear () { + // TODO: + } + + public Pattern.radial () { + // TODO: + } + + public Pattern copy () { + Pattern new_pattern = new Pattern (); + + new_pattern.type = this.type; + new_pattern.colors = this.colors; + new_pattern.hidden = this.hidden; + + return new_pattern; + } + + public void add_stop_color (Gdk.RGBA color, double offset) { + if (type == PatternType.SOLID) { + // Adding stop colors for solid pattern is not allowed. + assert (false); + } + + colors.add (StopColor () {offset = offset, color = color}); + } + + public Gdk.RGBA get_first_color () { + return colors.first ().color; + } + + private int are_equal (StopColor? first, StopColor? second) { + double thresh = 0.1; + + if (first.offset - second.offset < thresh) { + return -1; + } else if (first.offset - second.offset > thresh) { + return 1; + } + + return 0; + } + + public Pattern.deserialized (Json.Object obj) { + // TODO: + hidden = obj.get_boolean_member ("hidden"); + } + + public Json.Node serialize () { + // TODO: + var obj = new Json.Object (); + // obj.set_double_member ("r", rgba.red); + // obj.set_double_member ("g", rgba.green); + // obj.set_double_member ("b", rgba.blue); + // obj.set_double_member ("a", rgba.alpha); + // obj.set_boolean_member ("hidden", hidden); + var node = new Json.Node (Json.NodeType.OBJECT); + node.set_object (obj); + return node; + } +} From d944b2a7544365a51111894fd80a4b8e203484fe Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Mon, 18 Apr 2022 23:00:31 +0530 Subject: [PATCH 02/21] Refactor existing code to use Pattern instead of component --- src/Layouts/FillsList/FillItemModel.vala | 6 ++--- src/Lib/Components/CompiledFill.vala | 14 +++++------ src/Lib/Components/Fills.vala | 30 ++++++++++++++---------- src/Models/ColorModel.vala | 13 ++++------ src/Widgets/ColorButton.vala | 12 ++++++---- src/Widgets/ColorChooser.vala | 5 ++-- src/Widgets/ColorField.vala | 4 ++-- src/Widgets/EyeDropperButton.vala | 2 +- src/Widgets/OpacityField.vala | 9 +++---- src/meson.build | 3 +++ 10 files changed, 54 insertions(+), 44 deletions(-) diff --git a/src/Layouts/FillsList/FillItemModel.vala b/src/Layouts/FillsList/FillItemModel.vala index c924206f..30590079 100644 --- a/src/Layouts/FillsList/FillItemModel.vala +++ b/src/Layouts/FillsList/FillItemModel.vala @@ -39,9 +39,9 @@ public class Akira.Layouts.FillsList.FillItemModel : Models.ColorModel { var node = im.item_model.node_from_id (_cached_instance.id); assert (node != null); - var new_color = Lib.Components.Color.from_rgba (color, hidden); + var new_pattern = pattern.copy (); var new_fills = node.instance.components.fills.copy (); - new_fills.replace (Lib.Components.Fills.Fill (fill_id, new_color)); + new_fills.replace (Lib.Components.Fills.Fill (fill_id, new_pattern)); node.instance.components.fills = new_fills; im.item_model.alert_node_changed (node, Lib.Components.Component.Type.COMPILED_FILL); @@ -74,7 +74,7 @@ public class Akira.Layouts.FillsList.FillItemModel : Models.ColorModel { (blocker); var fill = _cached_instance.components.fills.fill_from_id (fill_id); - color = fill.color; + pattern = fill.pattern; hidden = fill.hidden; } } diff --git a/src/Lib/Components/CompiledFill.vala b/src/Lib/Components/CompiledFill.vala index 106b2387..395cc355 100644 --- a/src/Lib/Components/CompiledFill.vala +++ b/src/Lib/Components/CompiledFill.vala @@ -50,14 +50,14 @@ public class Akira.Lib.Components.CompiledFill : Copyable { bool has_colors = false; // Set an initial arbitrary color with full transparency. rgba_fill.alpha = 0; - + if (components == null) { return new CompiledFill (rgba_fill, has_colors); } - + unowned var fills = components.fills; unowned var opacity = components.opacity; - + if (fills == null) { return new CompiledFill (rgba_fill, has_colors); } @@ -68,18 +68,18 @@ public class Akira.Lib.Components.CompiledFill : Copyable { if (fills.data[i].hidden) { continue; } - + // Set the new blended color. - rgba_fill = Utils.Color.blend_colors (rgba_fill, fills.data[i].color); + rgba_fill = Utils.Color.blend_colors (rgba_fill, fills.data[i].pattern.get_first_color ()); has_colors = true; } - + // Apply the mixed RGBA value only if we had one. if (has_colors && opacity != null) { // Keep in consideration the global opacity to properly update the fill color. rgba_fill.alpha = rgba_fill.alpha * opacity.opacity / 100; } - + return new CompiledFill (rgba_fill, has_colors); } } diff --git a/src/Lib/Components/Fills.vala b/src/Lib/Components/Fills.vala index 76e6e52b..b92d4d85 100644 --- a/src/Lib/Components/Fills.vala +++ b/src/Lib/Components/Fills.vala @@ -22,16 +22,16 @@ public class Akira.Lib.Components.Fills : Component, Copyable { public struct Fill { public int _id; - public Color _color; + public Pattern _pattern; - public Fill (int id = -1, Color color = Color ()) { + public Fill (int id = -1, Pattern pattern = new Pattern ()) { _id = id; - _color = color; + _pattern = pattern; } public Fill.deserialized (int id, Json.Object obj) { _id = id; - _color = Color.deserialized (obj.get_object_member ("color")); + _pattern = new Pattern.deserialized (obj.get_object_member ("pattern")); } // Recommended accessors. @@ -40,26 +40,29 @@ public class Akira.Lib.Components.Fills : Component, Copyable { return _id; } } - public Gdk.RGBA color { + public Pattern pattern { get { - return _color.rgba; + return _pattern; } } public bool hidden { get { - return _color.hidden; + return _pattern.hidden; } } // Mutators. - public Fill with_color (Color new_color) { - return Fill (_id, new_color); + public Fill with_color (Color new_color, int id = 0) { + Pattern pattern = new Pattern.solid (new_color.rgba, new_color.hidden); + var fill = Fill (id, pattern); + fill._id = id; + return fill; } public Json.Node serialize () { var obj = new Json.Object (); obj.set_int_member ("id", _id); - obj.set_member ("color", _color.serialize ()); + obj.set_member ("pattern", _pattern.serialize ()); var node = new Json.Node (Json.NodeType.OBJECT); node.set_object (obj); return node; @@ -74,7 +77,7 @@ public class Akira.Lib.Components.Fills : Component, Copyable { public Fills.with_color (Color color) { data = new Fill[1]; - data[0] = Fill (0, color); + data[0] = Fill (0, new Pattern.solid (color.rgba, color.hidden)); } public Fills.deserialized (Json.Object obj) { @@ -110,7 +113,7 @@ public class Akira.Lib.Components.Fills : Component, Copyable { public Fill? fill_from_id (int id) { foreach (unowned var fill in data) { if (fill.id == id) { - return fill.with_color (fill._color); + return Fill (id, fill.pattern); } } return null; @@ -136,7 +139,8 @@ public class Akira.Lib.Components.Fills : Component, Copyable { latest_id = double.max (latest_id, fill.id); } latest_id++; - var fill = Fill ((int) latest_id, color); + var pattern = new Pattern.solid (color.rgba, color.hidden); + var fill = Fill ((int) latest_id, pattern); data.resize (data.length + 1); data[data.length - 1] = fill; } diff --git a/src/Models/ColorModel.vala b/src/Models/ColorModel.vala index 4364d3df..74624ed6 100644 --- a/src/Models/ColorModel.vala +++ b/src/Models/ColorModel.vala @@ -41,17 +41,14 @@ public class Akira.Models.ColorModel : GLib.Object { protected int block_signal = 0; - private Gdk.RGBA _color; - public Gdk.RGBA color { + private Lib.Components.Pattern _pattern; + public Lib.Components.Pattern pattern { get { - return _color; + return _pattern; } - set { - if (value == _color) { - return; - } - _color = value; + set { + _pattern = value; on_value_changed (); value_changed (); } diff --git a/src/Widgets/ColorButton.vala b/src/Widgets/ColorButton.vala index d989af93..2f44ae8d 100644 --- a/src/Widgets/ColorButton.vala +++ b/src/Widgets/ColorButton.vala @@ -76,7 +76,8 @@ public class Akira.Widgets.ColorButton : Gtk.Button { private void on_model_changed () { sensitive = !model.hidden; - var new_color = model.color.to_string (); + // var new_color = model.color.to_string (); + var new_color = model.pattern.get_first_color ().to_string (); if (new_color == current_color) { return; } @@ -104,11 +105,11 @@ public class Akira.Widgets.ColorButton : Gtk.Button { } color_chooser = new ColorChooser (); - color_chooser.color_changed.connect (color => { + color_chooser.pattern_changed.connect (pattern => { if (block_signal > 0) { return; } - model.color = color; + model.pattern = pattern; }); color_popover.add (color_chooser); } @@ -120,7 +121,10 @@ public class Akira.Widgets.ColorButton : Gtk.Button { var blocker = new SignalBlocker (this); (blocker); - color_chooser.set_color (model.color); + + Gdk.RGBA rgba = model.pattern.get_first_color (); + + color_chooser.set_color (rgba); color_popover.popup (); } } diff --git a/src/Widgets/ColorChooser.vala b/src/Widgets/ColorChooser.vala index dea66091..d0712202 100644 --- a/src/Widgets/ColorChooser.vala +++ b/src/Widgets/ColorChooser.vala @@ -23,7 +23,7 @@ * Helper class to create a container for the GtkColorChooser. */ public class Akira.Widgets.ColorChooser : Gtk.Grid { - public signal void color_changed (Gdk.RGBA color); + public signal void pattern_changed (Lib.Components.Pattern pattern); private Gtk.ColorChooserWidget chooser; private Gtk.FlowBox global_flowbox; @@ -134,6 +134,7 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { } private void on_color_changed () { - color_changed (chooser.get_rgba ()); + var pattern = new Lib.Components.Pattern.solid (chooser.get_rgba (), false); + pattern_changed (pattern); } } diff --git a/src/Widgets/ColorField.vala b/src/Widgets/ColorField.vala index aaa0aef3..28b52898 100644 --- a/src/Widgets/ColorField.vala +++ b/src/Widgets/ColorField.vala @@ -72,7 +72,7 @@ public class Akira.Widgets.ColorField : Gtk.Entry { var blocker = new SignalBlocker (this); (blocker); - text = Utils.Color.rgba_to_hex_string (model.color); + text = Utils.Color.rgba_to_hex_string (model.pattern.get_first_color ()); sensitive = !model.hidden; } @@ -87,7 +87,7 @@ public class Akira.Widgets.ColorField : Gtk.Entry { } var new_rgba = Utils.Color.hex_to_rgba (text); - model.color = new_rgba; + model.pattern = new Lib.Components.Pattern.solid (new_rgba, false); } private void on_insert_text (string text, int length, ref int position) { diff --git a/src/Widgets/EyeDropperButton.vala b/src/Widgets/EyeDropperButton.vala index 11ac5e5e..d495dedc 100644 --- a/src/Widgets/EyeDropperButton.vala +++ b/src/Widgets/EyeDropperButton.vala @@ -56,7 +56,7 @@ public class Akira.Widgets.EyeDropperButton : Gtk.Button { eyedropper.show_all (); eyedropper.picked.connect ((picked_color) => { - model.color = picked_color; + model.pattern = new Lib.Components.Pattern.solid (picked_color, false); eyedropper.close (); }); diff --git a/src/Widgets/OpacityField.vala b/src/Widgets/OpacityField.vala index 3855977c..11e64146 100644 --- a/src/Widgets/OpacityField.vala +++ b/src/Widgets/OpacityField.vala @@ -76,9 +76,9 @@ public class Akira.Widgets.OpacityField : Gtk.Grid { return; } - var new_color = model.color; + var new_color = model.pattern.get_first_color (); new_color.alpha = field.entry.value / 100; - model.color = new_color; + model.pattern = new Lib.Components.Pattern.solid (new_color, false); } /* @@ -86,12 +86,13 @@ public class Akira.Widgets.OpacityField : Gtk.Grid { */ private void on_model_changed () { sensitive = !model.hidden; - if (field.entry.value / 100 == model.color.alpha) { + var color = model.pattern.get_first_color (); + if (field.entry.value / 100 == color.alpha) { return; } var blocker = new SignalBlocker (this); (blocker); - field.entry.value = Math.round (model.color.alpha * 100); + field.entry.value = Math.round (color.alpha * 100); } } diff --git a/src/meson.build b/src/meson.build index 993b0926..bbe95ddc 100644 --- a/src/meson.build +++ b/src/meson.build @@ -82,6 +82,8 @@ sources = files( 'Widgets/OpacityField.vala', 'Widgets/RoundedColorButton.vala', 'Widgets/ZoomButton.vala', + 'Widgets/GradientEditor.vala', + 'Widgets/PatternChooser.vala', 'Widgets/VirtualizingListBox/VirtualizingListBox.vala', 'Widgets/VirtualizingListBox/VirtualizingListBoxModel.vala', @@ -134,6 +136,7 @@ sources = files( 'Lib/Components/Size.vala', 'Lib/Components/Text.vala', 'Lib/Components/Transform.vala', + 'Lib/Components/Pattern.vala', 'Lib/Items/Model.vala', 'Lib/Items/ModelInstance.vala', From 030036447c96700277b79a5811daa81a67e50226 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Mon, 18 Apr 2022 23:00:56 +0530 Subject: [PATCH 03/21] Basic widgets for Editing gradients --- src/Widgets/GradientEditor.vala | 43 +++++++++++++++++++++++++++++++++ src/Widgets/PatternChooser.vala | 41 +++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 src/Widgets/GradientEditor.vala create mode 100644 src/Widgets/PatternChooser.vala diff --git a/src/Widgets/GradientEditor.vala b/src/Widgets/GradientEditor.vala new file mode 100644 index 00000000..72820660 --- /dev/null +++ b/src/Widgets/GradientEditor.vala @@ -0,0 +1,43 @@ + +/** + * Copyright (c) 2022 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: Ashish Shevale + */ + +/* + * This class creates a widget that allows user to create new stop points, + * select existing stop points and assign them colors. + * + * When a different PatternType is selected from PatternChooser, this widget + * will redraw using the stop colors of that pattern. + * + * In order to visualize the pattern, the background of this widget will be colored + * with a linear gradient. + * + * It is disabled for SOLID pattern type. + */ +public class Akira.Widgets.GradientEditor : Gtk.DrawingArea { + // This signal will be triggered when pattern is editor using this editor. + // The PatternChooser will handle this signal. + public signal void pattern_editor (Lib.Components.Pattern pattern); + + construct { + + } +} diff --git a/src/Widgets/PatternChooser.vala b/src/Widgets/PatternChooser.vala new file mode 100644 index 00000000..53e389e5 --- /dev/null +++ b/src/Widgets/PatternChooser.vala @@ -0,0 +1,41 @@ + +/** + * Copyright (c) 2022 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: Ashish Shevale + */ + +/* + * This widget provides a set of options that allow user to select what kind of pattern + * they want to draw. + * Selecting ony one of the buttons will change the pattern type (SOLID, LINEAR, RADIAL). + */ +public class Akira.Widgets.PatternChooser : Granite.Widgets.ModeButton { + // Trigger this signal when the active PatternType changes or gradient editor changes. + public signal void pattern_changed (Lib.Components.Pattern pattern); + + construct { + construct_chooser_widget (); + } + + private void construct_chooser_widget () { + this.append_text ("Solid"); + this.append_text ("Linear"); + this.append_text ("Radial"); + } +} From 11efd54913139456595dc600baaae3fabb81de25 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Tue, 19 Apr 2022 22:02:48 +0530 Subject: [PATCH 04/21] Use Cairo.Pattern for painting --- src/Drawables/Drawable.vala | 4 +-- src/Lib/Components/CompiledFill.vala | 33 +++++++++++++----------- src/Lib/Components/Pattern.vala | 20 ++++++++++++-- src/Lib/Items/ModelTypeArtboard.vala | 4 +-- src/Lib/Items/ModelTypeEllipse.vala | 4 +-- src/Lib/Items/ModelTypePath.vala | 4 +-- src/Lib/Items/ModelTypeRect.vala | 4 +-- src/Lib/Items/ModelTypeText.vala | 4 +-- src/ViewLayers/ViewLayerMultiSelect.vala | 5 +++- 9 files changed, 52 insertions(+), 30 deletions(-) diff --git a/src/Drawables/Drawable.vala b/src/Drawables/Drawable.vala index 42d4b787..0800f3a4 100644 --- a/src/Drawables/Drawable.vala +++ b/src/Drawables/Drawable.vala @@ -43,7 +43,7 @@ public class Akira.Drawables.Drawable { // Style public double line_width { get; set; default = 0; } - public Gdk.RGBA fill_rgba { get; set; default = Gdk.RGBA (); } + public Cairo.Pattern fill_pattern { get; set; default = new Cairo.Pattern.rgba (255, 255, 255, 255); } public Gdk.RGBA stroke_rgba { get; set; default = Gdk.RGBA (); } public BorderType border_type { get; set; default = BorderType.CENTER; } public double radius_tr { get; set; default = 0; } @@ -383,7 +383,7 @@ public class Akira.Drawables.Drawable { if (draw_type == DrawType.XRAY) { return false; } - context.set_source_rgba (fill_rgba.red, fill_rgba.green, fill_rgba.blue, fill_rgba.alpha); + context.set_source (fill_pattern); context.set_antialias (Cairo.Antialias.GRAY); return true; } diff --git a/src/Lib/Components/CompiledFill.vala b/src/Lib/Components/CompiledFill.vala index 395cc355..98fd01eb 100644 --- a/src/Lib/Components/CompiledFill.vala +++ b/src/Lib/Components/CompiledFill.vala @@ -20,46 +20,44 @@ */ public class Akira.Lib.Components.CompiledFill : Copyable { - private Gdk.RGBA _color; + private Pattern _pattern; private bool _visible; - public Gdk.RGBA color { - get { return _color; } + public Pattern pattern { + get { return _pattern; } } public bool is_visible { get { return _visible; } } - public CompiledFill (Gdk.RGBA color, bool visible) { - _color = color; + public CompiledFill (Pattern pattern, bool visible) { + _pattern = pattern; _visible = visible; } public CompiledFill.as_empty () { - _color = Gdk.RGBA () { red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0}; + _pattern = new Pattern.solid (Gdk.RGBA () {red = 0, green = 0, blue = 0, alpha = 0}, false); _visible = false; } public CompiledFill copy () { - return new CompiledFill (_color, _visible); + return new CompiledFill (_pattern, _visible); } public static CompiledFill compile (Components? components, Lib.Items.ModelNode? node) { - var rgba_fill = Gdk.RGBA (); + var pattern_fill = new Pattern (); bool has_colors = false; - // Set an initial arbitrary color with full transparency. - rgba_fill.alpha = 0; if (components == null) { - return new CompiledFill (rgba_fill, has_colors); + return new CompiledFill (pattern_fill, has_colors); } unowned var fills = components.fills; unowned var opacity = components.opacity; if (fills == null) { - return new CompiledFill (rgba_fill, has_colors); + return new CompiledFill (pattern_fill, has_colors); } // Loop through all the configured fills. @@ -70,16 +68,21 @@ public class Akira.Lib.Components.CompiledFill : Copyable { } // Set the new blended color. - rgba_fill = Utils.Color.blend_colors (rgba_fill, fills.data[i].pattern.get_first_color ()); + // rgba_fill = Utils.Color.blend_colors (rgba_fill, fills.data[i].pattern.get_first_color ()); + pattern_fill = fills.data[i].pattern; has_colors = true; + + // TODO: Temporarily disable blending patterns. Not implemented. + break; } // Apply the mixed RGBA value only if we had one. if (has_colors && opacity != null) { // Keep in consideration the global opacity to properly update the fill color. - rgba_fill.alpha = rgba_fill.alpha * opacity.opacity / 100; + // TODO: Disable this too. + // rgba_fill.alpha = rgba_fill.alpha * opacity.opacity / 100; } - return new CompiledFill (rgba_fill, has_colors); + return new CompiledFill (pattern_fill, has_colors); } } diff --git a/src/Lib/Components/Pattern.vala b/src/Lib/Components/Pattern.vala index f38c9d8d..03f40649 100644 --- a/src/Lib/Components/Pattern.vala +++ b/src/Lib/Components/Pattern.vala @@ -37,6 +37,14 @@ public class Akira.Lib.Components.Pattern { public Gee.TreeSet colors; public bool hidden; + // These values denote the position of guide points of gradients. + // The values are relative to the canvas item's position. + public Geometry.Point start; + public Geometry.Point end; + // These values are only used for radial gradients to denote radii. + public double radius_start; + public double radius_end; + public Pattern.solid (Gdk.RGBA color, bool hidden) { this.type = PatternType.SOLID; this.hidden = hidden; @@ -45,8 +53,16 @@ public class Akira.Lib.Components.Pattern { colors.add (StopColor () {offset = 0, color = color}); } - public Pattern.linear () { - // TODO: + public Pattern.linear (Geometry.Point start, Geometry.Point end, bool hidden) { + this.start = start; + this.end = end; + + // By default, all linear gradients will be created with black and white colors at start and end. + colors.add (StopColor () {offset = 0, color = Gdk.RGBA () {red = 0, green = 0, blue = 0, alpha = 255}}); + colors.add (StopColor () {offset = 1, color = Gdk.RGBA () {red = 255, green = 255, blue = 255, alpha = 255}}); + + this.type = PatternType.LINEAR; + this.hidden = hidden; } public Pattern.radial () { diff --git a/src/Lib/Items/ModelTypeArtboard.vala b/src/Lib/Items/ModelTypeArtboard.vala index 536efb6c..8d3f0735 100644 --- a/src/Lib/Items/ModelTypeArtboard.vala +++ b/src/Lib/Items/ModelTypeArtboard.vala @@ -61,11 +61,11 @@ public class Akira.Lib.Items.ModelTypeArtboard : ModelType { switch (type) { case Lib.Components.Component.Type.COMPILED_FILL: if (!instance.compiled_fill.is_visible) { - instance.drawable.fill_rgba = Gdk.RGBA () { alpha = 0 }; + instance.drawable.fill_pattern = Utils.Pattern.default_pattern (); break; } - instance.drawable.fill_rgba = instance.compiled_fill.color; + instance.drawable.fill_pattern = Utils.Pattern.convert_to_cairo_pattern (instance.compiled_fill.pattern); break; case Lib.Components.Component.Type.COMPILED_GEOMETRY: instance.drawable.width = instance.components.size.width; diff --git a/src/Lib/Items/ModelTypeEllipse.vala b/src/Lib/Items/ModelTypeEllipse.vala index 9e47d71e..5abaefef 100644 --- a/src/Lib/Items/ModelTypeEllipse.vala +++ b/src/Lib/Items/ModelTypeEllipse.vala @@ -72,11 +72,11 @@ public class Akira.Lib.Items.ModelTypeEllipse : ModelType { break; case Lib.Components.Component.Type.COMPILED_FILL: if (!instance.compiled_fill.is_visible) { - instance.drawable.fill_rgba = Gdk.RGBA () { alpha = 0 }; + instance.drawable.fill_pattern = Utils.Pattern.default_pattern (); break; } - instance.drawable.fill_rgba = instance.compiled_fill.color; + instance.drawable.fill_pattern = Utils.Pattern.convert_to_cairo_pattern (instance.compiled_fill.pattern); break; case Lib.Components.Component.Type.COMPILED_GEOMETRY: var ellipse = instance.drawable as Drawables.DrawableEllipse; diff --git a/src/Lib/Items/ModelTypePath.vala b/src/Lib/Items/ModelTypePath.vala index d96a94c8..9bee369b 100644 --- a/src/Lib/Items/ModelTypePath.vala +++ b/src/Lib/Items/ModelTypePath.vala @@ -82,11 +82,11 @@ public class Akira.Lib.Items.ModelTypePath : ModelType { break; case Lib.Components.Component.Type.COMPILED_FILL: if (!instance.compiled_fill.is_visible) { - instance.drawable.fill_rgba = Gdk.RGBA () { alpha = 0 }; + instance.drawable.fill_pattern = Utils.Pattern.default_pattern (); break; } - instance.drawable.fill_rgba = instance.compiled_fill.color; + instance.drawable.fill_pattern = Utils.Pattern.convert_to_cairo_pattern (instance.compiled_fill.pattern); break; case Lib.Components.Component.Type.COMPILED_GEOMETRY: // The points property is only available to DrawablePath, so first typecast it diff --git a/src/Lib/Items/ModelTypeRect.vala b/src/Lib/Items/ModelTypeRect.vala index c0938452..7110d4cf 100644 --- a/src/Lib/Items/ModelTypeRect.vala +++ b/src/Lib/Items/ModelTypeRect.vala @@ -77,11 +77,11 @@ public class Akira.Lib.Items.ModelTypeRect : ModelType { break; case Lib.Components.Component.Type.COMPILED_FILL: if (!instance.compiled_fill.is_visible) { - instance.drawable.fill_rgba = Gdk.RGBA () { alpha = 0 }; + instance.drawable.fill_pattern = Utils.Pattern.default_pattern (); break; } - instance.drawable.fill_rgba = instance.compiled_fill.color; + instance.drawable.fill_pattern = Utils.Pattern.convert_to_cairo_pattern (instance.compiled_fill.pattern); break; case Lib.Components.Component.Type.COMPILED_GEOMETRY: instance.drawable.width = instance.components.size.width; diff --git a/src/Lib/Items/ModelTypeText.vala b/src/Lib/Items/ModelTypeText.vala index 445039ac..a61a8d10 100644 --- a/src/Lib/Items/ModelTypeText.vala +++ b/src/Lib/Items/ModelTypeText.vala @@ -76,11 +76,11 @@ public class Akira.Lib.Items.ModelTypeText : ModelType { break; case Lib.Components.Component.Type.COMPILED_FILL: if (!instance.compiled_fill.is_visible) { - instance.drawable.fill_rgba = Gdk.RGBA () { alpha = 0 }; + instance.drawable.fill_pattern = Utils.Pattern.default_pattern (); break; } - instance.drawable.fill_rgba = instance.compiled_fill.color; + instance.drawable.fill_pattern = Utils.Pattern.convert_to_cairo_pattern (instance.compiled_fill.pattern); break; case Lib.Components.Component.Type.COMPILED_GEOMETRY: instance.drawable.width = instance.components.size.width; diff --git a/src/ViewLayers/ViewLayerMultiSelect.vala b/src/ViewLayers/ViewLayerMultiSelect.vala index 9620131d..d2f1adec 100644 --- a/src/ViewLayers/ViewLayerMultiSelect.vala +++ b/src/ViewLayers/ViewLayerMultiSelect.vala @@ -23,6 +23,9 @@ public class Akira.ViewLayers.ViewLayerMultiSelect : ViewLayer { private const double UI_LINE_WIDTH = 1.0; private Gdk.RGBA fill { get; default = Gdk.RGBA () { red = 0.25, green = 0.79, blue = 0.98, alpha = 0.2 }; } + private Lib.Components.Pattern fill_pattern { + get; + default = new Lib.Components.Pattern.solid (Gdk.RGBA () { red = 0.25, green = 0.79, blue = 0.98, alpha = 0.2 }, false); } private Gdk.RGBA stroke { get { var color = fill; @@ -91,7 +94,7 @@ public class Akira.ViewLayers.ViewLayerMultiSelect : ViewLayer { return; } - drawable.fill_rgba = fill; + drawable.fill_pattern = Utils.Pattern.convert_to_cairo_pattern (fill_pattern); drawable.line_width = UI_LINE_WIDTH / scale; drawable.stroke_rgba = stroke; drawable.paint (context, target_bounds, scale, Drawables.Drawable.DrawType.NORMAL); From 114724775559f14b3efcda1fc7a7b0e1534188c1 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Tue, 19 Apr 2022 22:03:31 +0530 Subject: [PATCH 05/21] Create Utility methods for handling patterns: --- src/Utils/Pattern.vala | 78 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/Utils/Pattern.vala diff --git a/src/Utils/Pattern.vala b/src/Utils/Pattern.vala new file mode 100644 index 00000000..189eefef --- /dev/null +++ b/src/Utils/Pattern.vala @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2019-2021 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: Martin "mbfraga" Fraga + */ + +public class Akira.Utils.Pattern { + public static Cairo.Pattern convert_to_cairo_pattern (Lib.Components.Pattern pattern) { + Cairo.Pattern converted; + + switch (pattern.type) { + case Lib.Components.Pattern.PatternType.SOLID: + var color = pattern.colors.first ().color; + converted = new Cairo.Pattern.rgba (color.red, color.green, color.blue, color.alpha); + break; + case Lib.Components.Pattern.PatternType.LINEAR: + converted = new Cairo.Pattern.linear (pattern.start.x, pattern.start.y, pattern.end.x, pattern.end.y); + + foreach (var stop_color in pattern.colors) { + var color = stop_color.color; + converted.add_color_stop_rgba ( + stop_color.offset, + color.red, + color.green, + color.blue, + color.alpha + ); + } + break; + case Lib.Components.Pattern.PatternType.RADIAL: + converted = new Cairo.Pattern.radial ( + pattern.start.x, + pattern.start.y, + pattern.radius_start, + pattern.end.x, + pattern.end.y, + pattern.radius_end + ); + + foreach (var stop_color in pattern.colors) { + var color = stop_color.color; + converted.add_color_stop_rgba ( + stop_color.offset, + color.red, + color.green, + color.blue, + color.alpha + ); + } + break; + default: + assert (false); + converted = new Cairo.Pattern.rgba (0, 0, 0, 0); + break; + } + + return converted; + } + + public static Cairo.Pattern default_pattern () { + return new Cairo.Pattern.rgba (255, 255, 255, 255); + } +} \ No newline at end of file From 765b91aef455ec6e9384e2cf4d38e039ecd0ad43 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Wed, 20 Apr 2022 23:13:36 +0530 Subject: [PATCH 06/21] Implement gradient editor. Modify pattern when mode is changed --- src/Layouts/FillsList/FillItemModel.vala | 7 ++- src/Lib/Components/Fills.vala | 60 ++++++++++++++++++++++-- src/Lib/Components/Pattern.vala | 7 +-- src/Models/ColorModel.vala | 36 ++++++++++++-- src/Utils/Pattern.vala | 4 +- src/Widgets/ColorButton.vala | 6 +-- src/Widgets/ColorChooser.vala | 28 +++++++---- src/Widgets/GradientEditor.vala | 49 ++++++++++++++++++- src/Widgets/PatternChooser.vala | 22 ++++++++- src/meson.build | 1 + 10 files changed, 193 insertions(+), 27 deletions(-) diff --git a/src/Layouts/FillsList/FillItemModel.vala b/src/Layouts/FillsList/FillItemModel.vala index 30590079..d53f1f04 100644 --- a/src/Layouts/FillsList/FillItemModel.vala +++ b/src/Layouts/FillsList/FillItemModel.vala @@ -74,7 +74,12 @@ public class Akira.Layouts.FillsList.FillItemModel : Models.ColorModel { (blocker); var fill = _cached_instance.components.fills.fill_from_id (fill_id); - pattern = fill.pattern; + active_pattern_type = fill.active_pattern; + + solid_pattern = fill.solid_pattern; + linear_pattern = fill.linear_pattern; + radial_pattern = fill.radial_pattern; + hidden = fill.hidden; } } diff --git a/src/Lib/Components/Fills.vala b/src/Lib/Components/Fills.vala index b92d4d85..a1236fa1 100644 --- a/src/Lib/Components/Fills.vala +++ b/src/Lib/Components/Fills.vala @@ -22,16 +22,70 @@ public class Akira.Lib.Components.Fills : Component, Copyable { public struct Fill { public int _id; - public Pattern _pattern; + public Pattern _pattern { + get { + switch (active_pattern) { + case Pattern.PatternType.SOLID: + return solid_pattern; + case Pattern.PatternType.LINEAR: + return linear_pattern; + case Pattern.PatternType.RADIAL: + return radial_pattern; + default: + return solid_pattern; + } + } + + set { + switch (active_pattern) { + case Pattern.PatternType.SOLID: + solid_pattern = value; + break; + case Pattern.PatternType.LINEAR: + linear_pattern = value; + break; + case Pattern.PatternType.RADIAL: + radial_pattern = value; + break; + default: + solid_pattern = value; + break; + } + } + } + + // Each fill item will have patterns for all three types, + // so that user can easily switch between them. + // However, the non active patterns will not be serialized. + public Pattern solid_pattern; + public Pattern linear_pattern; + public Pattern radial_pattern; + + public Pattern.PatternType active_pattern; public Fill (int id = -1, Pattern pattern = new Pattern ()) { _id = id; - _pattern = pattern; + active_pattern = Pattern.PatternType.SOLID; + + var fill_rgba = Gdk.RGBA (); + fill_rgba.parse (settings.fill_color); + solid_pattern = new Pattern.solid (fill_rgba, false); + + linear_pattern = new Pattern.linear (Geometry.Point (0, 0), Geometry.Point (100, 100), false); + radial_pattern = new Pattern.radial (); } public Fill.deserialized (int id, Json.Object obj) { _id = id; - _pattern = new Pattern.deserialized (obj.get_object_member ("pattern")); + // active_pattern = Pattern.PatternType.SOLID; + + // solid_pattern = new Pattern.solid (0, 0, 0, 255); + + // linear_pattern = new Pattern.linear (Geometry.Point (0, 0), Geometry.Point (100, 100), false); + // linear_pattern.add_stop_color (Gdk.RGBA () {red = 0, green = 0, blue = 0, alpha = 0}, 0); + // linear_pattern.add_stop_color (Gdk.RGBA () {red = 255, green = 255, blue = 255, alpha = 0}, 1); + + // radial_pattern = new Pattern.radial (); } // Recommended accessors. diff --git a/src/Lib/Components/Pattern.vala b/src/Lib/Components/Pattern.vala index 03f40649..6e6a51ce 100644 --- a/src/Lib/Components/Pattern.vala +++ b/src/Lib/Components/Pattern.vala @@ -22,9 +22,9 @@ public class Akira.Lib.Components.Pattern { public enum PatternType { - SOLID, - LINEAR, - RADIAL, + SOLID = 0, + LINEAR = 1, + RADIAL = 2, } public struct StopColor { @@ -58,6 +58,7 @@ public class Akira.Lib.Components.Pattern { this.end = end; // By default, all linear gradients will be created with black and white colors at start and end. + colors = new Gee.TreeSet (are_equal); colors.add (StopColor () {offset = 0, color = Gdk.RGBA () {red = 0, green = 0, blue = 0, alpha = 255}}); colors.add (StopColor () {offset = 1, color = Gdk.RGBA () {red = 255, green = 255, blue = 255, alpha = 255}}); diff --git a/src/Models/ColorModel.vala b/src/Models/ColorModel.vala index 74624ed6..b35f68ab 100644 --- a/src/Models/ColorModel.vala +++ b/src/Models/ColorModel.vala @@ -41,14 +41,44 @@ public class Akira.Models.ColorModel : GLib.Object { protected int block_signal = 0; - private Lib.Components.Pattern _pattern; + // All three types of patterns will be stored here. + // Based on which type is active, update it. + public Lib.Components.Pattern solid_pattern; + public Lib.Components.Pattern linear_pattern; + public Lib.Components.Pattern radial_pattern; + + public Lib.Components.Pattern.PatternType active_pattern_type; + public Lib.Components.Pattern pattern { get { - return _pattern; + switch (active_pattern_type) { + case Lib.Components.Pattern.PatternType.SOLID: + return solid_pattern; + case Lib.Components.Pattern.PatternType.LINEAR: + return linear_pattern; + case Lib.Components.Pattern.PatternType.RADIAL: + return radial_pattern; + default: + return solid_pattern; + } } set { - _pattern = value; + switch (active_pattern_type) { + case Lib.Components.Pattern.PatternType.SOLID: + solid_pattern = value; + break; + case Lib.Components.Pattern.PatternType.LINEAR: + linear_pattern = value; + break; + case Lib.Components.Pattern.PatternType.RADIAL: + radial_pattern = value; + break; + default: + solid_pattern = value; + break; + } + on_value_changed (); value_changed (); } diff --git a/src/Utils/Pattern.vala b/src/Utils/Pattern.vala index 189eefef..2c65a068 100644 --- a/src/Utils/Pattern.vala +++ b/src/Utils/Pattern.vala @@ -1,5 +1,5 @@ /** - * Copyright (c) 2019-2021 Alecaddd (https://alecaddd.com) + * Copyright (c) 2022 Alecaddd (https://alecaddd.com) * * This file is part of Akira. * @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with Akira. If not, see . * - * Authored by: Martin "mbfraga" Fraga + * Authored by: Ashish Shevale */ public class Akira.Utils.Pattern { diff --git a/src/Widgets/ColorButton.vala b/src/Widgets/ColorButton.vala index 2f44ae8d..7b2420ef 100644 --- a/src/Widgets/ColorButton.vala +++ b/src/Widgets/ColorButton.vala @@ -104,7 +104,7 @@ public class Akira.Widgets.ColorButton : Gtk.Button { return; } - color_chooser = new ColorChooser (); + color_chooser = new ColorChooser (model); color_chooser.pattern_changed.connect (pattern => { if (block_signal > 0) { return; @@ -122,9 +122,7 @@ public class Akira.Widgets.ColorButton : Gtk.Button { var blocker = new SignalBlocker (this); (blocker); - Gdk.RGBA rgba = model.pattern.get_first_color (); - - color_chooser.set_color (rgba); + color_chooser.set_pattern (model.pattern); color_popover.popup (); } } diff --git a/src/Widgets/ColorChooser.vala b/src/Widgets/ColorChooser.vala index d0712202..9f622fd3 100644 --- a/src/Widgets/ColorChooser.vala +++ b/src/Widgets/ColorChooser.vala @@ -27,6 +27,8 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { private Gtk.ColorChooserWidget chooser; private Gtk.FlowBox global_flowbox; + private GradientEditor gradient_editor; + private PatternChooser pattern_chooser; /* * Type of color containers to add new colors to. We can potentially create @@ -37,11 +39,21 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { DOCUMENT } - public ColorChooser () { + public ColorChooser (Models.ColorModel model) { margin_top = margin_bottom = 12; margin_start = margin_end = 3; row_spacing = 12; get_style_context ().add_class ("color-picker"); + + pattern_chooser = new PatternChooser (model); + attach (pattern_chooser, 0, 0, 1, 1); + + gradient_editor = new GradientEditor (model.pattern.colors); + attach (gradient_editor, 0, 1, 1, 1); + + pattern_chooser.pattern_changed.connect ((pattern) => { + gradient_editor.pattern_edited (pattern); + }); chooser = new Gtk.ColorChooserWidget () { hexpand = true, @@ -49,13 +61,13 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { }; chooser.notify["rgba"].connect (on_color_changed); - attach (chooser, 0, 0, 1, 1); + attach (chooser, 0, 2, 1, 1); var global_label = new Gtk.Label (_("Global colors")) { halign = Gtk.Align.START, margin_start = margin_end = 6 }; - attach (global_label, 0, 1, 1, 1); + attach (global_label, 0, 3, 1, 1); global_flowbox = new Gtk.FlowBox () { selection_mode = Gtk.SelectionMode.NONE, @@ -79,7 +91,7 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { global_flowbox.add (btn); } - attach (global_flowbox, 0, 2, 1, 1); + attach (global_flowbox, 0, 4, 1, 1); show_all (); } @@ -129,12 +141,12 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { return (a is AddColorButton) ? -1 : 1; } - public void set_color (Gdk.RGBA color) { - chooser.set_rgba (color); + public void set_pattern (Lib.Components.Pattern pattern) { + pattern_chooser.pattern_changed (pattern); + gradient_editor.pattern_edited (pattern); } private void on_color_changed () { - var pattern = new Lib.Components.Pattern.solid (chooser.get_rgba (), false); - pattern_changed (pattern); + gradient_editor.color_changed (chooser.get_rgba ()); } } diff --git a/src/Widgets/GradientEditor.vala b/src/Widgets/GradientEditor.vala index 72820660..f22872a5 100644 --- a/src/Widgets/GradientEditor.vala +++ b/src/Widgets/GradientEditor.vala @@ -35,9 +35,54 @@ public class Akira.Widgets.GradientEditor : Gtk.DrawingArea { // This signal will be triggered when pattern is editor using this editor. // The PatternChooser will handle this signal. - public signal void pattern_editor (Lib.Components.Pattern pattern); + public signal void pattern_edited (Lib.Components.Pattern pattern); + public signal void color_changed (Gdk.RGBA color); - construct { + // Dimensions of the widget. + private double width; + private double height; + + private unowned Gee.TreeSet stop_colors; + + public GradientEditor (Gee.TreeSet stop_colors) { + hexpand = true; + height_request = 40; + margin = 5; + + this.stop_colors = stop_colors; + size_allocate.connect (() => { + width = get_allocated_width (); + height = get_allocated_height (); + + draw.connect (draw_editor); + }); + + pattern_edited.connect ((pattern) => { + this.stop_colors = pattern.colors; + queue_draw (); + }); + } + + private bool draw_editor (Cairo.Context context) { + context.set_source_rgba (255, 255, 0, 255); + context.move_to (0, 0); + context.rectangle (0, 0, width, height); + context.stroke (); + + var pattern = new Lib.Components.Pattern.linear (Geometry.Point (0, height / 2.0), Geometry.Point (width, height / 2.0), false); + pattern.colors = stop_colors; + + print("Colors\n"); + foreach (var s in this.stop_colors) { + print(": %f\n", s.offset); + } + + var converted_pattern = Utils.Pattern.convert_to_cairo_pattern (pattern); + context.set_source (converted_pattern); + context.rectangle (0, 0, width, height); + context.fill (); + + return true; } } diff --git a/src/Widgets/PatternChooser.vala b/src/Widgets/PatternChooser.vala index 53e389e5..ca98e363 100644 --- a/src/Widgets/PatternChooser.vala +++ b/src/Widgets/PatternChooser.vala @@ -29,8 +29,20 @@ public class Akira.Widgets.PatternChooser : Granite.Widgets.ModeButton { // Trigger this signal when the active PatternType changes or gradient editor changes. public signal void pattern_changed (Lib.Components.Pattern pattern); - construct { + private Models.ColorModel model; + + public PatternChooser (Models.ColorModel model) { construct_chooser_widget (); + + this.set_active ((int) model.pattern.type); + this.model = model; + + // Connect signals. + this.mode_changed.connect (handle_mode_changed); + } + + public void set_pattern_type (Lib.Components.Pattern.PatternType type) { + this.set_active ((int) type); } private void construct_chooser_widget () { @@ -38,4 +50,12 @@ public class Akira.Widgets.PatternChooser : Granite.Widgets.ModeButton { this.append_text ("Linear"); this.append_text ("Radial"); } + + private void handle_mode_changed (Gtk.Widget widget) { + var active_mode = (Lib.Components.Pattern.PatternType) this.selected; + + model.active_pattern_type = active_mode; + + pattern_changed (model.pattern); + } } diff --git a/src/meson.build b/src/meson.build index bbe95ddc..eab93f7d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -43,6 +43,7 @@ sources = files( 'Utils/GeometryMath.vala', 'Utils/Bezier.vala', 'Utils/Delegates.vala', + 'Utils/Pattern.vala', 'Layouts/HeaderBar.vala', 'Layouts/MainViewCanvas.vala', From bb2943192e4adf0709eec94a40857d55cc51e273 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Thu, 21 Apr 2022 10:53:47 +0530 Subject: [PATCH 07/21] Refactor PatternTypeChooser --- src/Widgets/ColorChooser.vala | 4 ++-- src/Widgets/{PatternChooser.vala => PatternTypeChooser.vala} | 4 ++-- src/meson.build | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/Widgets/{PatternChooser.vala => PatternTypeChooser.vala} (93%) diff --git a/src/Widgets/ColorChooser.vala b/src/Widgets/ColorChooser.vala index 9f622fd3..551d71da 100644 --- a/src/Widgets/ColorChooser.vala +++ b/src/Widgets/ColorChooser.vala @@ -28,7 +28,7 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { private Gtk.ColorChooserWidget chooser; private Gtk.FlowBox global_flowbox; private GradientEditor gradient_editor; - private PatternChooser pattern_chooser; + private PatternTypeChooser pattern_chooser; /* * Type of color containers to add new colors to. We can potentially create @@ -45,7 +45,7 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { row_spacing = 12; get_style_context ().add_class ("color-picker"); - pattern_chooser = new PatternChooser (model); + pattern_chooser = new PatternTypeChooser (model); attach (pattern_chooser, 0, 0, 1, 1); gradient_editor = new GradientEditor (model.pattern.colors); diff --git a/src/Widgets/PatternChooser.vala b/src/Widgets/PatternTypeChooser.vala similarity index 93% rename from src/Widgets/PatternChooser.vala rename to src/Widgets/PatternTypeChooser.vala index ca98e363..f96b3774 100644 --- a/src/Widgets/PatternChooser.vala +++ b/src/Widgets/PatternTypeChooser.vala @@ -25,13 +25,13 @@ * they want to draw. * Selecting ony one of the buttons will change the pattern type (SOLID, LINEAR, RADIAL). */ -public class Akira.Widgets.PatternChooser : Granite.Widgets.ModeButton { +public class Akira.Widgets.PatternTypeChooser : Granite.Widgets.ModeButton { // Trigger this signal when the active PatternType changes or gradient editor changes. public signal void pattern_changed (Lib.Components.Pattern pattern); private Models.ColorModel model; - public PatternChooser (Models.ColorModel model) { + public PatternTypeChooser (Models.ColorModel model) { construct_chooser_widget (); this.set_active ((int) model.pattern.type); diff --git a/src/meson.build b/src/meson.build index eab93f7d..7c37d528 100644 --- a/src/meson.build +++ b/src/meson.build @@ -84,7 +84,7 @@ sources = files( 'Widgets/RoundedColorButton.vala', 'Widgets/ZoomButton.vala', 'Widgets/GradientEditor.vala', - 'Widgets/PatternChooser.vala', + 'Widgets/PatternTypeChooser.vala', 'Widgets/VirtualizingListBox/VirtualizingListBox.vala', 'Widgets/VirtualizingListBox/VirtualizingListBoxModel.vala', From e6fef5e8d1ad644e01ac94aa76c9bef320448bc0 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Thu, 21 Apr 2022 12:15:05 +0530 Subject: [PATCH 08/21] Paint patterns on Canvas --- src/Layouts/FillsList/FillItemModel.vala | 7 +++--- src/Lib/Components/CompiledFill.vala | 12 +++++----- src/Lib/Components/Fills.vala | 14 ++++++++++- src/Models/ColorModel.vala | 17 +++++++++++--- src/Utils/Pattern.vala | 30 ++++++++++++------------ src/ViewLayers/ViewLayerMultiSelect.vala | 4 ++-- src/Widgets/ColorButton.vala | 2 +- src/Widgets/ColorChooser.vala | 2 +- src/Widgets/GradientEditor.vala | 14 +++++------ 9 files changed, 63 insertions(+), 39 deletions(-) diff --git a/src/Layouts/FillsList/FillItemModel.vala b/src/Layouts/FillsList/FillItemModel.vala index d53f1f04..902742a4 100644 --- a/src/Layouts/FillsList/FillItemModel.vala +++ b/src/Layouts/FillsList/FillItemModel.vala @@ -41,7 +41,8 @@ public class Akira.Layouts.FillsList.FillItemModel : Models.ColorModel { var new_pattern = pattern.copy (); var new_fills = node.instance.components.fills.copy (); - new_fills.replace (Lib.Components.Fills.Fill (fill_id, new_pattern)); + var new_fill = new_fills.fill_from_id (fill_id).with_replaced_pattern (new_pattern); + new_fills.replace (new_fill); node.instance.components.fills = new_fills; im.item_model.alert_node_changed (node, Lib.Components.Component.Type.COMPILED_FILL); @@ -75,11 +76,11 @@ public class Akira.Layouts.FillsList.FillItemModel : Models.ColorModel { var fill = _cached_instance.components.fills.fill_from_id (fill_id); active_pattern_type = fill.active_pattern; - + solid_pattern = fill.solid_pattern; linear_pattern = fill.linear_pattern; radial_pattern = fill.radial_pattern; - + hidden = fill.hidden; } } diff --git a/src/Lib/Components/CompiledFill.vala b/src/Lib/Components/CompiledFill.vala index 98fd01eb..8645026b 100644 --- a/src/Lib/Components/CompiledFill.vala +++ b/src/Lib/Components/CompiledFill.vala @@ -48,14 +48,14 @@ public class Akira.Lib.Components.CompiledFill : Copyable { public static CompiledFill compile (Components? components, Lib.Items.ModelNode? node) { var pattern_fill = new Pattern (); bool has_colors = false; - + if (components == null) { return new CompiledFill (pattern_fill, has_colors); } - + unowned var fills = components.fills; unowned var opacity = components.opacity; - + if (fills == null) { return new CompiledFill (pattern_fill, has_colors); } @@ -66,7 +66,7 @@ public class Akira.Lib.Components.CompiledFill : Copyable { if (fills.data[i].hidden) { continue; } - + // Set the new blended color. // rgba_fill = Utils.Color.blend_colors (rgba_fill, fills.data[i].pattern.get_first_color ()); pattern_fill = fills.data[i].pattern; @@ -75,14 +75,14 @@ public class Akira.Lib.Components.CompiledFill : Copyable { // TODO: Temporarily disable blending patterns. Not implemented. break; } - + // Apply the mixed RGBA value only if we had one. if (has_colors && opacity != null) { // Keep in consideration the global opacity to properly update the fill color. // TODO: Disable this too. // rgba_fill.alpha = rgba_fill.alpha * opacity.opacity / 100; } - + return new CompiledFill (pattern_fill, has_colors); } } diff --git a/src/Lib/Components/Fills.vala b/src/Lib/Components/Fills.vala index a1236fa1..6bae132f 100644 --- a/src/Lib/Components/Fills.vala +++ b/src/Lib/Components/Fills.vala @@ -84,7 +84,7 @@ public class Akira.Lib.Components.Fills : Component, Copyable { // linear_pattern = new Pattern.linear (Geometry.Point (0, 0), Geometry.Point (100, 100), false); // linear_pattern.add_stop_color (Gdk.RGBA () {red = 0, green = 0, blue = 0, alpha = 0}, 0); // linear_pattern.add_stop_color (Gdk.RGBA () {red = 255, green = 255, blue = 255, alpha = 0}, 1); - + // radial_pattern = new Pattern.radial (); } @@ -113,6 +113,18 @@ public class Akira.Lib.Components.Fills : Component, Copyable { return fill; } + public Fill with_replaced_pattern (Pattern new_pattern) { + var new_fill = new Fill (); + + new_fill._id = this._id; + new_fill.active_pattern = new_pattern.type; + new_fill.solid_pattern = this.solid_pattern; + new_fill.linear_pattern = this.linear_pattern; + new_fill.radial_pattern = this.radial_pattern; + + return new_fill; + } + public Json.Node serialize () { var obj = new Json.Object (); obj.set_int_member ("id", _id); diff --git a/src/Models/ColorModel.vala b/src/Models/ColorModel.vala index b35f68ab..096d263d 100644 --- a/src/Models/ColorModel.vala +++ b/src/Models/ColorModel.vala @@ -47,11 +47,22 @@ public class Akira.Models.ColorModel : GLib.Object { public Lib.Components.Pattern linear_pattern; public Lib.Components.Pattern radial_pattern; - public Lib.Components.Pattern.PatternType active_pattern_type; + public Lib.Components.Pattern.PatternType _active_pattern_type; + public Lib.Components.Pattern.PatternType active_pattern_type { + get { + return _active_pattern_type; + } + + set { + _active_pattern_type = value; + on_value_changed (); + value_changed (); + } + } public Lib.Components.Pattern pattern { get { - switch (active_pattern_type) { + switch (_active_pattern_type) { case Lib.Components.Pattern.PatternType.SOLID: return solid_pattern; case Lib.Components.Pattern.PatternType.LINEAR: @@ -64,7 +75,7 @@ public class Akira.Models.ColorModel : GLib.Object { } set { - switch (active_pattern_type) { + switch (_active_pattern_type) { case Lib.Components.Pattern.PatternType.SOLID: solid_pattern = value; break; diff --git a/src/Utils/Pattern.vala b/src/Utils/Pattern.vala index 2c65a068..e56a2928 100644 --- a/src/Utils/Pattern.vala +++ b/src/Utils/Pattern.vala @@ -34,31 +34,31 @@ public class Akira.Utils.Pattern { foreach (var stop_color in pattern.colors) { var color = stop_color.color; converted.add_color_stop_rgba ( - stop_color.offset, - color.red, - color.green, - color.blue, + stop_color.offset, + color.red, + color.green, + color.blue, color.alpha ); } break; case Lib.Components.Pattern.PatternType.RADIAL: converted = new Cairo.Pattern.radial ( - pattern.start.x, - pattern.start.y, - pattern.radius_start, - pattern.end.x, - pattern.end.y, + pattern.start.x, + pattern.start.y, + pattern.radius_start, + pattern.end.x, + pattern.end.y, pattern.radius_end ); - + foreach (var stop_color in pattern.colors) { var color = stop_color.color; converted.add_color_stop_rgba ( - stop_color.offset, - color.red, - color.green, - color.blue, + stop_color.offset, + color.red, + color.green, + color.blue, color.alpha ); } @@ -75,4 +75,4 @@ public class Akira.Utils.Pattern { public static Cairo.Pattern default_pattern () { return new Cairo.Pattern.rgba (255, 255, 255, 255); } -} \ No newline at end of file +} diff --git a/src/ViewLayers/ViewLayerMultiSelect.vala b/src/ViewLayers/ViewLayerMultiSelect.vala index d2f1adec..a9a6f354 100644 --- a/src/ViewLayers/ViewLayerMultiSelect.vala +++ b/src/ViewLayers/ViewLayerMultiSelect.vala @@ -23,8 +23,8 @@ public class Akira.ViewLayers.ViewLayerMultiSelect : ViewLayer { private const double UI_LINE_WIDTH = 1.0; private Gdk.RGBA fill { get; default = Gdk.RGBA () { red = 0.25, green = 0.79, blue = 0.98, alpha = 0.2 }; } - private Lib.Components.Pattern fill_pattern { - get; + private Lib.Components.Pattern fill_pattern { + get; default = new Lib.Components.Pattern.solid (Gdk.RGBA () { red = 0.25, green = 0.79, blue = 0.98, alpha = 0.2 }, false); } private Gdk.RGBA stroke { get { diff --git a/src/Widgets/ColorButton.vala b/src/Widgets/ColorButton.vala index 7b2420ef..b5ef94d1 100644 --- a/src/Widgets/ColorButton.vala +++ b/src/Widgets/ColorButton.vala @@ -121,7 +121,7 @@ public class Akira.Widgets.ColorButton : Gtk.Button { var blocker = new SignalBlocker (this); (blocker); - + color_chooser.set_pattern (model.pattern); color_popover.popup (); } diff --git a/src/Widgets/ColorChooser.vala b/src/Widgets/ColorChooser.vala index 551d71da..e0402396 100644 --- a/src/Widgets/ColorChooser.vala +++ b/src/Widgets/ColorChooser.vala @@ -44,7 +44,7 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { margin_start = margin_end = 3; row_spacing = 12; get_style_context ().add_class ("color-picker"); - + pattern_chooser = new PatternTypeChooser (model); attach (pattern_chooser, 0, 0, 1, 1); diff --git a/src/Widgets/GradientEditor.vala b/src/Widgets/GradientEditor.vala index f22872a5..8a85c016 100644 --- a/src/Widgets/GradientEditor.vala +++ b/src/Widgets/GradientEditor.vala @@ -50,7 +50,7 @@ public class Akira.Widgets.GradientEditor : Gtk.DrawingArea { margin = 5; this.stop_colors = stop_colors; - + size_allocate.connect (() => { width = get_allocated_width (); height = get_allocated_height (); @@ -70,13 +70,13 @@ public class Akira.Widgets.GradientEditor : Gtk.DrawingArea { context.rectangle (0, 0, width, height); context.stroke (); - var pattern = new Lib.Components.Pattern.linear (Geometry.Point (0, height / 2.0), Geometry.Point (width, height / 2.0), false); - pattern.colors = stop_colors; + var pattern = new Lib.Components.Pattern.linear ( + Geometry.Point (0, height / 2.0), + Geometry.Point (width, height / 2.0), + false + ); - print("Colors\n"); - foreach (var s in this.stop_colors) { - print(": %f\n", s.offset); - } + pattern.colors = stop_colors; var converted_pattern = Utils.Pattern.convert_to_cairo_pattern (pattern); context.set_source (converted_pattern); From 56e653a197072874ba60a5ea12edb2c92282bb3d Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Sat, 23 Apr 2022 10:32:45 +0530 Subject: [PATCH 09/21] Draw nobs on canvas for gradients --- src/Layouts/FillsList/FillListItem.vala | 2 +- src/Lib/Components/Fills.vala | 4 +-- src/Lib/Managers/NobManager.vala | 26 ++++++++++++++++++ src/Services/EventBus.vala | 1 + src/Utils/Nobs.vala | 24 ++++++++++++++--- src/ViewLayers/ViewLayerNobs.vala | 36 ++++++++++++++++++++++--- src/Widgets/ColorButton.vala | 13 +++++++-- src/Widgets/ColorChooser.vala | 4 +-- src/Widgets/PatternTypeChooser.vala | 35 ++++++++++++++++++++++-- 9 files changed, 129 insertions(+), 16 deletions(-) diff --git a/src/Layouts/FillsList/FillListItem.vala b/src/Layouts/FillsList/FillListItem.vala index 9d2bf915..2ba6a8bc 100644 --- a/src/Layouts/FillsList/FillListItem.vala +++ b/src/Layouts/FillsList/FillListItem.vala @@ -50,7 +50,7 @@ public class Akira.Layouts.FillsList.FillListItem : VirtualizingListBoxRow { context.add_class ("selected-color-container"); context.add_class ("bg-pattern"); - color_button = new Widgets.ColorButton (); + color_button = new Widgets.ColorButton (view_canvas.window); container.add (color_button); eyedropper_button = new Widgets.EyeDropperButton () {}; diff --git a/src/Lib/Components/Fills.vala b/src/Lib/Components/Fills.vala index 6bae132f..a185ae00 100644 --- a/src/Lib/Components/Fills.vala +++ b/src/Lib/Components/Fills.vala @@ -71,7 +71,7 @@ public class Akira.Lib.Components.Fills : Component, Copyable { fill_rgba.parse (settings.fill_color); solid_pattern = new Pattern.solid (fill_rgba, false); - linear_pattern = new Pattern.linear (Geometry.Point (0, 0), Geometry.Point (100, 100), false); + linear_pattern = new Pattern.linear (Geometry.Point (5, 5), Geometry.Point (95, 95), false); radial_pattern = new Pattern.radial (); } @@ -114,7 +114,7 @@ public class Akira.Lib.Components.Fills : Component, Copyable { } public Fill with_replaced_pattern (Pattern new_pattern) { - var new_fill = new Fill (); + var new_fill = Fill (); new_fill._id = this._id; new_fill.active_pattern = new_pattern.type; diff --git a/src/Lib/Managers/NobManager.vala b/src/Lib/Managers/NobManager.vala index 550b904e..26b62910 100644 --- a/src/Lib/Managers/NobManager.vala +++ b/src/Lib/Managers/NobManager.vala @@ -38,6 +38,10 @@ public class Akira.Lib.Managers.NobManager : Object { public NobManager (Lib.ViewCanvas canvas) { Object (view_canvas: canvas); + + view_canvas.window.event_bus.change_gradient_nobs_visibility.connect ((visible) => { + print("Change visibility\n"); + }); } construct { @@ -155,6 +159,28 @@ public class Akira.Lib.Managers.NobManager : Object { maybe_create_anchor_point_effect (); } + public void set_gradient_nob_position (Utils.Nobs.Nob nob, Geometry.Point position) { + nobs.data[nob].center_x = position.x; + nobs.data[nob].center_y = position.y; + } + + // This method will set the render flags for ViewLayerNobs and redraw the layer. + // It must be called only after positions of all nobs have been set. + public void set_layer_flags_from_pattern_type (Lib.Components.Pattern.PatternType ptype) { + if (ptype == Lib.Components.Pattern.PatternType.SOLID) { + nob_layer.render_gradient_nobs = false; + nob_layer.render_gradient_radii_nobs = false; + } else if (ptype == Lib.Components.Pattern.PatternType.LINEAR) { + nob_layer.render_gradient_nobs = true; + nob_layer.render_gradient_radii_nobs = false; + } else if (ptype == Lib.Components.Pattern.PatternType.RADIAL) { + nob_layer.render_gradient_nobs = true; + nob_layer.render_gradient_radii_nobs = true; + } + + nob_layer.update_nob_data (nobs); + } + private void remove_anchor_point_effect () { nob_layer.add_anchor_point (null); } diff --git a/src/Services/EventBus.vala b/src/Services/EventBus.vala index c3ddbc5f..8aa821de 100644 --- a/src/Services/EventBus.vala +++ b/src/Services/EventBus.vala @@ -48,6 +48,7 @@ public class Akira.Services.EventBus : Object { public signal void update_snaps_color (); public signal void update_snap_decorators (); public signal void zoom_changed (double new_zoom); + public signal void change_gradient_nobs_visibility (bool visible); // Canvas triggers. public signal void adjust_zoom (double zoom, bool absolute, Geometry.Point? reference); diff --git a/src/Utils/Nobs.vala b/src/Utils/Nobs.vala index 9a2b4597..ec5751e9 100644 --- a/src/Utils/Nobs.vala +++ b/src/Utils/Nobs.vala @@ -43,6 +43,10 @@ public class Akira.Utils.Nobs : Object { BOTTOM_LEFT, LEFT_CENTER, ROTATE, + GRADIENT_START, + GRADIENT_END, + GRADIENT_RADIUS_START, + GRADIENT_RADIUS_END, ALL } @@ -71,15 +75,15 @@ public class Akira.Utils.Nobs : Object { public NobData[] data; public NobSet () { - data = new NobData[9]; - for (var i = 0; i < 9; i++) { + data = new NobData[13]; + for (var i = 0; i < 13; i++) { data[i] = new NobData ((Nob)i, 0, 0, false); } } public NobSet.clone (NobSet other) { - data = new NobData[9]; - for (var i = 0; i < 9; i++) { + data = new NobData[13]; + for (var i = 0; i < 13; i++) { data[i] = other.data[i].copy (); } } @@ -205,6 +209,18 @@ public class Akira.Utils.Nobs : Object { case Nob.ROTATE: result = Gdk.CursorType.EXCHANGE; break; + case Nob.GRADIENT_START: + result = Gdk.CursorType.TCROSS; + break; + case Nob.GRADIENT_END: + result = Gdk.CursorType.TCROSS; + break; + case Nob.GRADIENT_RADIUS_START: + result = Gdk.CursorType.TCROSS; + break; + case Nob.GRADIENT_RADIUS_END: + result = Gdk.CursorType.TCROSS; + break; default: break; } diff --git a/src/ViewLayers/ViewLayerNobs.vala b/src/ViewLayers/ViewLayerNobs.vala index 58c1d5a6..b83dcb99 100644 --- a/src/ViewLayers/ViewLayerNobs.vala +++ b/src/ViewLayers/ViewLayerNobs.vala @@ -33,6 +33,11 @@ public class Akira.ViewLayers.ViewLayerNobs : ViewLayer { private Drawables.Drawable? old_anchor_point_drawable = null; private Geometry.Rectangle anchor_point_last_bb_drawn = Geometry.Rectangle.empty (); + // If this is true, draw the start and end nobs for linear and radial gradients. + public bool render_gradient_nobs = false; + // If this is true, draw the radius nobs. Only use for radial gradients. + public bool render_gradient_radii_nobs = false; + public void update_nob_data (Utils.Nobs.NobSet? new_nobs) { if (nobs != null) { old_nobs = new Utils.Nobs.NobSet.clone (nobs); @@ -102,6 +107,26 @@ public class Akira.ViewLayers.ViewLayerNobs : ViewLayer { if (!nob.active) { continue; } + + // Render gradient nobs only if the proper flags are set. + if ( + nob.handle_id == Utils.Nobs.Nob.GRADIENT_START || + nob.handle_id == Utils.Nobs.Nob.GRADIENT_END + ) { + if (!render_gradient_nobs) { + continue; + } + } + + if ( + nob.handle_id == Utils.Nobs.Nob.GRADIENT_RADIUS_START || + nob.handle_id == Utils.Nobs.Nob.GRADIENT_RADIUS_END + ) { + if (!render_gradient_radii_nobs) { + continue; + } + } + context.save (); context.new_path (); @@ -109,10 +134,15 @@ public class Akira.ViewLayers.ViewLayerNobs : ViewLayer { context.set_line_width (line_width); context.translate (nob.center_x, nob.center_y); - if (nob.handle_id == Utils.Nobs.Nob.ROTATE) { + if ( + nob.handle_id == Utils.Nobs.Nob.ROTATE || + nob.handle_id == Utils.Nobs.Nob.GRADIENT_START || + nob.handle_id == Utils.Nobs.Nob.GRADIENT_END || + nob.handle_id == Utils.Nobs.Nob.GRADIENT_RADIUS_START || + nob.handle_id == Utils.Nobs.Nob.GRADIENT_RADIUS_END + ) { context.arc (0, 0, radius, 0, 2.0 * GLib.Math.PI); - } - else { + } else { double x = -radius; double w = radius * 2; double y = -radius; diff --git a/src/Widgets/ColorButton.vala b/src/Widgets/ColorButton.vala index b5ef94d1..bf6ed6f4 100644 --- a/src/Widgets/ColorButton.vala +++ b/src/Widgets/ColorButton.vala @@ -25,6 +25,7 @@ */ public class Akira.Widgets.ColorButton : Gtk.Button { private unowned Models.ColorModel model; + private unowned Window window; private Gtk.Popover color_popover; private Widgets.ColorChooser? color_chooser = null; @@ -46,7 +47,9 @@ public class Akira.Widgets.ColorButton : Gtk.Button { protected int block_signal = 0; - public ColorButton () { + public ColorButton (Window window) { + this.window = window; + get_style_context ().add_class ("selected-color"); vexpand = true; width_request = 40; @@ -58,6 +61,10 @@ public class Akira.Widgets.ColorButton : Gtk.Button { }; clicked.connect (on_clicked); + + color_popover.closed.connect (() => { + window.event_bus.change_gradient_nobs_visibility (true); + }); } ~ColorButton () { @@ -104,7 +111,7 @@ public class Akira.Widgets.ColorButton : Gtk.Button { return; } - color_chooser = new ColorChooser (model); + color_chooser = new ColorChooser (model, window); color_chooser.pattern_changed.connect (pattern => { if (block_signal > 0) { return; @@ -124,5 +131,7 @@ public class Akira.Widgets.ColorButton : Gtk.Button { color_chooser.set_pattern (model.pattern); color_popover.popup (); + + window.event_bus.change_gradient_nobs_visibility (true); } } diff --git a/src/Widgets/ColorChooser.vala b/src/Widgets/ColorChooser.vala index e0402396..f5563ae9 100644 --- a/src/Widgets/ColorChooser.vala +++ b/src/Widgets/ColorChooser.vala @@ -39,13 +39,13 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { DOCUMENT } - public ColorChooser (Models.ColorModel model) { + public ColorChooser (Models.ColorModel model, Window window) { margin_top = margin_bottom = 12; margin_start = margin_end = 3; row_spacing = 12; get_style_context ().add_class ("color-picker"); - pattern_chooser = new PatternTypeChooser (model); + pattern_chooser = new PatternTypeChooser (model, window); attach (pattern_chooser, 0, 0, 1, 1); gradient_editor = new GradientEditor (model.pattern.colors); diff --git a/src/Widgets/PatternTypeChooser.vala b/src/Widgets/PatternTypeChooser.vala index f96b3774..9d931b27 100644 --- a/src/Widgets/PatternTypeChooser.vala +++ b/src/Widgets/PatternTypeChooser.vala @@ -26,12 +26,14 @@ * Selecting ony one of the buttons will change the pattern type (SOLID, LINEAR, RADIAL). */ public class Akira.Widgets.PatternTypeChooser : Granite.Widgets.ModeButton { + private unowned Lib.ViewCanvas canvas; + // Trigger this signal when the active PatternType changes or gradient editor changes. public signal void pattern_changed (Lib.Components.Pattern pattern); private Models.ColorModel model; - public PatternTypeChooser (Models.ColorModel model) { + public PatternTypeChooser (Models.ColorModel model, Window window) { construct_chooser_widget (); this.set_active ((int) model.pattern.type); @@ -39,6 +41,8 @@ public class Akira.Widgets.PatternTypeChooser : Granite.Widgets.ModeButton { // Connect signals. this.mode_changed.connect (handle_mode_changed); + + this.canvas = window.main_window.main_view_canvas.canvas; } public void set_pattern_type (Lib.Components.Pattern.PatternType type) { @@ -55,7 +59,34 @@ public class Akira.Widgets.PatternTypeChooser : Granite.Widgets.ModeButton { var active_mode = (Lib.Components.Pattern.PatternType) this.selected; model.active_pattern_type = active_mode; - pattern_changed (model.pattern); + + var coords = canvas.selection_manager.selection.first_node ().instance.components.center; + var size = canvas.selection_manager.selection.first_node ().instance.components.size; + + Geometry.Point origin = Geometry.Point (coords.x - size.width / 2.0, coords.y - size.height / 2.0); + + // Update position of nobs in ViewLayerNobs. + Geometry.Point hidden_pos = Geometry.Point (0, 0); + Geometry.Point start_nob_pos = Geometry.Point (model.pattern.start.x + origin.x, model.pattern.start.y + origin.y); + Geometry.Point end_nob_pos = Geometry.Point (model.pattern.end.x + origin.x, model.pattern.end.y + origin.y); + + print("Start pos %f %f\n", start_nob_pos.x, start_nob_pos.y); + print("End pos %f %f\n", end_nob_pos.x, end_nob_pos.y); + + canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_START, hidden_pos); + canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_END, hidden_pos); + canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_RADIUS_START, hidden_pos); + canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_RADIUS_END, hidden_pos); + + if (active_mode == Lib.Components.Pattern.PatternType.LINEAR) { + canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_START, start_nob_pos); + canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_END, end_nob_pos); + } else if (active_mode == Lib.Components.Pattern.PatternType.RADIAL) { + canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_START, start_nob_pos); + canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_END, end_nob_pos); + } + + canvas.nob_manager.set_layer_flags_from_pattern_type (active_mode); } } From 61ef623629e0b1181dadc1e79a0a5f3be37d6b53 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Sat, 23 Apr 2022 13:55:45 +0530 Subject: [PATCH 10/21] Fix position of gradients on canvas --- src/Lib/Components/CompiledFill.vala | 4 +++- src/Lib/Components/Pattern.vala | 4 ++++ src/Lib/Managers/NobManager.vala | 8 +++++++- src/Utils/Pattern.vala | 18 ++++++++++++++++++ src/Widgets/ColorButton.vala | 3 ++- src/Widgets/PatternTypeChooser.vala | 15 +++++++++------ 6 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/Lib/Components/CompiledFill.vala b/src/Lib/Components/CompiledFill.vala index 8645026b..c4540698 100644 --- a/src/Lib/Components/CompiledFill.vala +++ b/src/Lib/Components/CompiledFill.vala @@ -55,6 +55,8 @@ public class Akira.Lib.Components.CompiledFill : Copyable { unowned var fills = components.fills; unowned var opacity = components.opacity; + unowned var size = components.size; + unowned var center = components.center; if (fills == null) { return new CompiledFill (pattern_fill, has_colors); @@ -69,7 +71,7 @@ public class Akira.Lib.Components.CompiledFill : Copyable { // Set the new blended color. // rgba_fill = Utils.Color.blend_colors (rgba_fill, fills.data[i].pattern.get_first_color ()); - pattern_fill = fills.data[i].pattern; + pattern_fill = Utils.Pattern.create_pattern_with_converted_positions (fills.data[i].pattern, size, center); has_colors = true; // TODO: Temporarily disable blending patterns. Not implemented. diff --git a/src/Lib/Components/Pattern.vala b/src/Lib/Components/Pattern.vala index 6e6a51ce..37954d1a 100644 --- a/src/Lib/Components/Pattern.vala +++ b/src/Lib/Components/Pattern.vala @@ -76,6 +76,10 @@ public class Akira.Lib.Components.Pattern { new_pattern.type = this.type; new_pattern.colors = this.colors; new_pattern.hidden = this.hidden; + new_pattern.start = this.start; + new_pattern.end = this.end; + new_pattern.radius_start = this.radius_start; + new_pattern.radius_end = this.radius_end; return new_pattern; } diff --git a/src/Lib/Managers/NobManager.vala b/src/Lib/Managers/NobManager.vala index 26b62910..f3e86259 100644 --- a/src/Lib/Managers/NobManager.vala +++ b/src/Lib/Managers/NobManager.vala @@ -40,7 +40,13 @@ public class Akira.Lib.Managers.NobManager : Object { Object (view_canvas: canvas); view_canvas.window.event_bus.change_gradient_nobs_visibility.connect ((visible) => { - print("Change visibility\n"); + if (visible) { + nob_layer.render_gradient_nobs = false; + nob_layer.render_gradient_radii_nobs = false; + } else { + nob_layer.render_gradient_nobs = true; + nob_layer.render_gradient_radii_nobs = true; + } }); } diff --git a/src/Utils/Pattern.vala b/src/Utils/Pattern.vala index e56a2928..8549f82b 100644 --- a/src/Utils/Pattern.vala +++ b/src/Utils/Pattern.vala @@ -75,4 +75,22 @@ public class Akira.Utils.Pattern { public static Cairo.Pattern default_pattern () { return new Cairo.Pattern.rgba (255, 255, 255, 255); } + + // In Components.Pattern, the start and end positions of gradients are stored as + // relative positions as percentages of width and height of canvas item. + // This method will convert those values to actual values, to be stored in CompiledFill. + public static Lib.Components.Pattern create_pattern_with_converted_positions (Lib.Components.Pattern pattern, Lib.Components.Size size, Lib.Components.Coordinates center) { + var new_pattern = pattern.copy (); + + new_pattern.start = Geometry.Point ( + pattern.start.x * size.width / 100.0 - size.width / 2.0, + pattern.start.y * size.height / 100.0 - size.height / 2.0 + ); + new_pattern.end = Geometry.Point ( + pattern.end.x * size.width / 100.0 - size.width / 2.0, + pattern.end.y * size.height / 100.0 - size.height / 2.0 + ); + + return new_pattern; + } } diff --git a/src/Widgets/ColorButton.vala b/src/Widgets/ColorButton.vala index bf6ed6f4..7a685121 100644 --- a/src/Widgets/ColorButton.vala +++ b/src/Widgets/ColorButton.vala @@ -59,11 +59,12 @@ public class Akira.Widgets.ColorButton : Gtk.Button { color_popover = new Gtk.Popover (this) { position = Gtk.PositionType.BOTTOM }; + color_popover.modal = true; clicked.connect (on_clicked); color_popover.closed.connect (() => { - window.event_bus.change_gradient_nobs_visibility (true); + window.event_bus.change_gradient_nobs_visibility (false); }); } diff --git a/src/Widgets/PatternTypeChooser.vala b/src/Widgets/PatternTypeChooser.vala index 9d931b27..4ee7bb5e 100644 --- a/src/Widgets/PatternTypeChooser.vala +++ b/src/Widgets/PatternTypeChooser.vala @@ -68,17 +68,20 @@ public class Akira.Widgets.PatternTypeChooser : Granite.Widgets.ModeButton { // Update position of nobs in ViewLayerNobs. Geometry.Point hidden_pos = Geometry.Point (0, 0); - Geometry.Point start_nob_pos = Geometry.Point (model.pattern.start.x + origin.x, model.pattern.start.y + origin.y); - Geometry.Point end_nob_pos = Geometry.Point (model.pattern.end.x + origin.x, model.pattern.end.y + origin.y); + var start_nob_pos = Geometry.Point ( + model.pattern.start.x * size.width / 100.0 + origin.x, + model.pattern.start.y * size.height / 100.0 + origin.y + ); + var end_nob_pos = Geometry.Point ( + model.pattern.end.x * size.width / 100.0 + origin.x, + model.pattern.end.y * size.height / 100.0 + origin.y + ); - print("Start pos %f %f\n", start_nob_pos.x, start_nob_pos.y); - print("End pos %f %f\n", end_nob_pos.x, end_nob_pos.y); - canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_START, hidden_pos); canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_END, hidden_pos); canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_RADIUS_START, hidden_pos); canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_RADIUS_END, hidden_pos); - + if (active_mode == Lib.Components.Pattern.PatternType.LINEAR) { canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_START, start_nob_pos); canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_END, end_nob_pos); From 938d48ff88d2d67aeab16c72bbe1a7236cdf6452 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Sun, 24 Apr 2022 20:29:07 +0530 Subject: [PATCH 11/21] Move gradients and nobs and update corressponsing pattern --- src/Layouts/FillsList/FillItemModel.vala | 33 ++++++++++++++++++++++++ src/Lib/Components/Fills.vala | 9 ++++++- src/Lib/Managers/NobManager.vala | 13 +++++++--- src/Lib/Modes/TransformMode.vala | 31 ++++++++++++++++++++++ src/Services/EventBus.vala | 1 + src/Utils/Nobs.vala | 9 +++++++ src/Widgets/ColorButton.vala | 2 +- src/Widgets/PatternTypeChooser.vala | 20 ++++++++++---- 8 files changed, 108 insertions(+), 10 deletions(-) diff --git a/src/Layouts/FillsList/FillItemModel.vala b/src/Layouts/FillsList/FillItemModel.vala index 902742a4..ef479db3 100644 --- a/src/Layouts/FillsList/FillItemModel.vala +++ b/src/Layouts/FillsList/FillItemModel.vala @@ -62,6 +62,39 @@ public class Akira.Layouts.FillsList.FillItemModel : Models.ColorModel { im.compile_model (); } + public void move_pattern_position_by_delta (Utils.Nobs.Nob nob, Geometry.Point delta) { + Geometry.Point percent_delta = Geometry.Point ( + delta.x * 100.0 / _cached_instance.components.size.width, + delta.y * 100.0 / _cached_instance.components.size.height + ); + + switch (nob) { + case Utils.Nobs.Nob.GRADIENT_START: + pattern.start = Geometry.Point ( + pattern.start.x - percent_delta.x, + pattern.start.y - percent_delta.y + ); + break; + case Utils.Nobs.Nob.GRADIENT_END: + pattern.end = Geometry.Point ( + pattern.end.x - percent_delta.x, + pattern.end.y - percent_delta.y + ); + break; + case Utils.Nobs.Nob.GRADIENT_RADIUS_START: + pattern.radius_start = pattern.radius_start + percent_delta.x; + break; + case Utils.Nobs.Nob.GRADIENT_RADIUS_END: + pattern.radius_end = pattern.radius_end + percent_delta.x; + break; + default: + break; + } + + on_value_changed (); + value_changed (); + } + public FillItemModel (Lib.ViewCanvas view_canvas, Lib.Items.ModelNode node, int fill_id) { update_node (node, fill_id); _view_canvas = view_canvas; diff --git a/src/Lib/Components/Fills.vala b/src/Lib/Components/Fills.vala index a185ae00..e7e9a2cf 100644 --- a/src/Lib/Components/Fills.vala +++ b/src/Lib/Components/Fills.vala @@ -75,6 +75,13 @@ public class Akira.Lib.Components.Fills : Component, Copyable { radial_pattern = new Pattern.radial (); } + public Fill.with_all_patterns (int id, Pattern solid_pattern, Pattern linear_pattern, Pattern radial_pattern, Pattern.PatternType type) { + this.solid_pattern = solid_pattern; + this.linear_pattern = linear_pattern; + this.radial_pattern = radial_pattern; + this.active_pattern = type; + } + public Fill.deserialized (int id, Json.Object obj) { _id = id; // active_pattern = Pattern.PatternType.SOLID; @@ -179,7 +186,7 @@ public class Akira.Lib.Components.Fills : Component, Copyable { public Fill? fill_from_id (int id) { foreach (unowned var fill in data) { if (fill.id == id) { - return Fill (id, fill.pattern); + return Fill.with_all_patterns (id, fill.solid_pattern, fill.linear_pattern, fill.radial_pattern, fill.active_pattern); } } return null; diff --git a/src/Lib/Managers/NobManager.vala b/src/Lib/Managers/NobManager.vala index f3e86259..917d6d6b 100644 --- a/src/Lib/Managers/NobManager.vala +++ b/src/Lib/Managers/NobManager.vala @@ -41,12 +41,14 @@ public class Akira.Lib.Managers.NobManager : Object { view_canvas.window.event_bus.change_gradient_nobs_visibility.connect ((visible) => { if (visible) { - nob_layer.render_gradient_nobs = false; - nob_layer.render_gradient_radii_nobs = false; - } else { nob_layer.render_gradient_nobs = true; nob_layer.render_gradient_radii_nobs = true; + } else { + nob_layer.render_gradient_nobs = false; + nob_layer.render_gradient_radii_nobs = false; } + + update_nob_layer (); }); } @@ -110,6 +112,11 @@ public class Akira.Lib.Managers.NobManager : Object { var active_nob_id = view_canvas.mode_manager.active_mode_nob; foreach (var nob in nobs.data) { + if (Utils.Nobs.is_gradient_nob (nob.handle_id)) { + nob.active = true; + continue; + } + bool set_visible = true; if (!show_h_centers && Utils.Nobs.is_horizontal_center (nob.handle_id)) { diff --git a/src/Lib/Modes/TransformMode.vala b/src/Lib/Modes/TransformMode.vala index 13f1f0e3..090629a6 100644 --- a/src/Lib/Modes/TransformMode.vala +++ b/src/Lib/Modes/TransformMode.vala @@ -180,6 +180,19 @@ public class Akira.Lib.Modes.TransformMode : AbstractInteractionMode { event.y ); break; + case Utils.Nobs.Nob.GRADIENT_START: + case Utils.Nobs.Nob.GRADIENT_END: + case Utils.Nobs.Nob.GRADIENT_RADIUS_START: + case Utils.Nobs.Nob.GRADIENT_RADIUS_END: + move_gradient_nob ( + view_canvas, + nob, + selection, + initial_drag_state, + event.x, + event.y + ); + break; default: effective_nob = scale_from_event ( view_canvas, @@ -537,4 +550,22 @@ public class Akira.Lib.Modes.TransformMode : AbstractInteractionMode { } } } + + private static void move_gradient_nob ( + Lib.ViewCanvas view_canvas, + Utils.Nobs.Nob nob, + Lib.Items.NodeSelection selection, + InitialDragState initial_drag_state, + double event_x, + double event_y + ) { + var position = Geometry.Point ( + initial_drag_state.press_x - event_x, + initial_drag_state.press_y - event_y + ); + initial_drag_state.press_x = event_x; + initial_drag_state.press_y = event_y; + + view_canvas.window.event_bus.translate_gradient_nob_by_delta (nob, position); + } } diff --git a/src/Services/EventBus.vala b/src/Services/EventBus.vala index 8aa821de..e121d62b 100644 --- a/src/Services/EventBus.vala +++ b/src/Services/EventBus.vala @@ -49,6 +49,7 @@ public class Akira.Services.EventBus : Object { public signal void update_snap_decorators (); public signal void zoom_changed (double new_zoom); public signal void change_gradient_nobs_visibility (bool visible); + public signal void translate_gradient_nob_by_delta (Utils.Nobs.Nob nob, Geometry.Point delta); // Canvas triggers. public signal void adjust_zoom (double zoom, bool absolute, Geometry.Point? reference); diff --git a/src/Utils/Nobs.vala b/src/Utils/Nobs.vala index ec5751e9..e59157a6 100644 --- a/src/Utils/Nobs.vala +++ b/src/Utils/Nobs.vala @@ -173,6 +173,15 @@ public class Akira.Utils.Nobs : Object { return (nob == Utils.Nobs.Nob.TOP_CENTER || nob == Utils.Nobs.Nob.BOTTOM_CENTER); } + public static bool is_gradient_nob (Nob nob) { + return ( + nob == Nob.GRADIENT_START || + nob == Nob.GRADIENT_END || + nob == Nob.GRADIENT_RADIUS_START || + nob == Nob.GRADIENT_RADIUS_END + ); + } + /* * Return a cursor type based of the type of nob. */ diff --git a/src/Widgets/ColorButton.vala b/src/Widgets/ColorButton.vala index 7a685121..fc8ee0ab 100644 --- a/src/Widgets/ColorButton.vala +++ b/src/Widgets/ColorButton.vala @@ -59,7 +59,7 @@ public class Akira.Widgets.ColorButton : Gtk.Button { color_popover = new Gtk.Popover (this) { position = Gtk.PositionType.BOTTOM }; - color_popover.modal = true; + color_popover.modal = false; clicked.connect (on_clicked); diff --git a/src/Widgets/PatternTypeChooser.vala b/src/Widgets/PatternTypeChooser.vala index 4ee7bb5e..9df79b3e 100644 --- a/src/Widgets/PatternTypeChooser.vala +++ b/src/Widgets/PatternTypeChooser.vala @@ -40,9 +40,22 @@ public class Akira.Widgets.PatternTypeChooser : Granite.Widgets.ModeButton { this.model = model; // Connect signals. - this.mode_changed.connect (handle_mode_changed); + this.mode_changed.connect ((window) => { + var active_mode = (Lib.Components.Pattern.PatternType) this.selected; + + model.active_pattern_type = active_mode; + pattern_changed (model.pattern); + + handle_pattern_changed (); + }); this.canvas = window.main_window.main_view_canvas.canvas; + + window.event_bus.translate_gradient_nob_by_delta.connect ((nob, delta) => { + ((Layouts.FillsList.FillItemModel) model).move_pattern_position_by_delta (nob, delta); + + handle_pattern_changed (); + }); } public void set_pattern_type (Lib.Components.Pattern.PatternType type) { @@ -55,12 +68,9 @@ public class Akira.Widgets.PatternTypeChooser : Granite.Widgets.ModeButton { this.append_text ("Radial"); } - private void handle_mode_changed (Gtk.Widget widget) { + private void handle_pattern_changed () { var active_mode = (Lib.Components.Pattern.PatternType) this.selected; - model.active_pattern_type = active_mode; - pattern_changed (model.pattern); - var coords = canvas.selection_manager.selection.first_node ().instance.components.center; var size = canvas.selection_manager.selection.first_node ().instance.components.size; From 887a59b4b35747fdf294d4dae97596641c31d91e Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Mon, 25 Apr 2022 11:17:52 +0530 Subject: [PATCH 12/21] Use selected pattern to draw the color button --- src/Utils/Pattern.vala | 17 +++++++++++++++++ src/Widgets/ColorButton.vala | 14 +++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/Utils/Pattern.vala b/src/Utils/Pattern.vala index 8549f82b..fff78f26 100644 --- a/src/Utils/Pattern.vala +++ b/src/Utils/Pattern.vala @@ -93,4 +93,21 @@ public class Akira.Utils.Pattern { return new_pattern; } + + public static string convert_to_css_linear_gradient (Lib.Components.Pattern pattern) { + if (pattern.type == Lib.Components.Pattern.PatternType.SOLID) { + var color = pattern.get_first_color ().to_string (); + return """linear-gradient(to right, %s, %s)""".printf (color, color); + } + + string css_result = "linear-gradient(to right"; + + foreach (var stop_color in pattern.colors) { + css_result += """ ,%s %f""".printf(stop_color.color.to_string (), stop_color.offset * 100.0); + css_result += "%"; + } + + css_result += ")"; + return css_result; + } } diff --git a/src/Widgets/ColorButton.vala b/src/Widgets/ColorButton.vala index fc8ee0ab..0aa61983 100644 --- a/src/Widgets/ColorButton.vala +++ b/src/Widgets/ColorButton.vala @@ -30,7 +30,7 @@ public class Akira.Widgets.ColorButton : Gtk.Button { private Gtk.Popover color_popover; private Widgets.ColorChooser? color_chooser = null; - private string? current_color = null; + private string? current_pattern = null; public class SignalBlocker { private unowned ColorButton item; @@ -85,8 +85,8 @@ public class Akira.Widgets.ColorButton : Gtk.Button { sensitive = !model.hidden; // var new_color = model.color.to_string (); - var new_color = model.pattern.get_first_color ().to_string (); - if (new_color == current_color) { + var new_pattern = Utils.Pattern.convert_to_css_linear_gradient (model.pattern); + if (new_pattern == current_pattern) { return; } @@ -95,13 +95,13 @@ public class Akira.Widgets.ColorButton : Gtk.Button { var context = get_style_context (); var css = """.selected-color { - background-color: %s; - border-color: shade (%s, 0.75); - }""".printf (new_color, new_color); + background-image: %s; + border: none; + }""".printf (new_pattern); provider.load_from_data (css, css.length); context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); - current_color = new_color; + current_pattern = new_pattern; } catch (Error e) { warning ("Style error: %s", e.message); } From 7c84698120a450b510320d8557e31b81e51ed4d4 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Mon, 25 Apr 2022 22:26:14 +0530 Subject: [PATCH 13/21] Use gradient editor to move stop colors on pattern component --- src/Lib/Components/Pattern.vala | 6 +- src/Utils/Pattern.vala | 2 +- src/Widgets/ColorChooser.vala | 8 +- src/Widgets/GradientEditor.vala | 145 +++++++++++++++++++++++++--- src/Widgets/PatternTypeChooser.vala | 2 +- 5 files changed, 144 insertions(+), 19 deletions(-) diff --git a/src/Lib/Components/Pattern.vala b/src/Lib/Components/Pattern.vala index 37954d1a..959dea93 100644 --- a/src/Lib/Components/Pattern.vala +++ b/src/Lib/Components/Pattern.vala @@ -98,11 +98,9 @@ public class Akira.Lib.Components.Pattern { } private int are_equal (StopColor? first, StopColor? second) { - double thresh = 0.1; - - if (first.offset - second.offset < thresh) { + if (first.offset < second.offset) { return -1; - } else if (first.offset - second.offset > thresh) { + } else if (first.offset > second.offset) { return 1; } diff --git a/src/Utils/Pattern.vala b/src/Utils/Pattern.vala index fff78f26..f215c19b 100644 --- a/src/Utils/Pattern.vala +++ b/src/Utils/Pattern.vala @@ -103,7 +103,7 @@ public class Akira.Utils.Pattern { string css_result = "linear-gradient(to right"; foreach (var stop_color in pattern.colors) { - css_result += """ ,%s %f""".printf(stop_color.color.to_string (), stop_color.offset * 100.0); + css_result += """ ,%s %f""".printf (stop_color.color.to_string (), stop_color.offset * 100.0); css_result += "%"; } diff --git a/src/Widgets/ColorChooser.vala b/src/Widgets/ColorChooser.vala index f5563ae9..6b192482 100644 --- a/src/Widgets/ColorChooser.vala +++ b/src/Widgets/ColorChooser.vala @@ -48,11 +48,15 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { pattern_chooser = new PatternTypeChooser (model, window); attach (pattern_chooser, 0, 0, 1, 1); - gradient_editor = new GradientEditor (model.pattern.colors); + gradient_editor = new GradientEditor (model.pattern); attach (gradient_editor, 0, 1, 1, 1); pattern_chooser.pattern_changed.connect ((pattern) => { - gradient_editor.pattern_edited (pattern); + gradient_editor.set_pattern (pattern); + }); + + gradient_editor.pattern_edited.connect ((pattern) => { + model.pattern = pattern; }); chooser = new Gtk.ColorChooserWidget () { diff --git a/src/Widgets/GradientEditor.vala b/src/Widgets/GradientEditor.vala index 8a85c016..7922daee 100644 --- a/src/Widgets/GradientEditor.vala +++ b/src/Widgets/GradientEditor.vala @@ -33,6 +33,9 @@ * It is disabled for SOLID pattern type. */ public class Akira.Widgets.GradientEditor : Gtk.DrawingArea { + public const double NOB_RADII = 5; + public const double STROKE_WIDTH = 0.5; + // This signal will be triggered when pattern is editor using this editor. // The PatternChooser will handle this signal. public signal void pattern_edited (Lib.Components.Pattern pattern); @@ -42,14 +45,22 @@ public class Akira.Widgets.GradientEditor : Gtk.DrawingArea { private double width; private double height; - private unowned Gee.TreeSet stop_colors; + // Represents all the stop colors of current pattern. + private Lib.Components.Pattern pattern; + // Represents currently active stop color. + private unowned Lib.Components.Pattern.StopColor selected_stop_color; - public GradientEditor (Gee.TreeSet stop_colors) { + public GradientEditor (Lib.Components.Pattern pattern) { hexpand = true; height_request = 40; margin = 5; + set_events ( + Gdk.EventMask.BUTTON_PRESS_MASK | + Gdk.EventMask.BUTTON_RELEASE_MASK | + Gdk.EventMask.BUTTON_MOTION_MASK + ); - this.stop_colors = stop_colors; + this.pattern = pattern; size_allocate.connect (() => { width = get_allocated_width (); @@ -58,31 +69,143 @@ public class Akira.Widgets.GradientEditor : Gtk.DrawingArea { draw.connect (draw_editor); }); - pattern_edited.connect ((pattern) => { - this.stop_colors = pattern.colors; - queue_draw (); - }); + this.button_press_event.connect (handle_button_press); + this.motion_notify_event.connect (handle_motion_notify); + this.button_release_event.connect (handle_button_release); + } + + public void set_pattern (Lib.Components.Pattern pattern) { + this.pattern = pattern; + queue_draw (); } private bool draw_editor (Cairo.Context context) { - context.set_source_rgba (255, 255, 0, 255); + draw_pattern (context); + draw_stop_colors (context); + + return true; + } + + private void draw_pattern (Cairo.Context context) { + context.set_source_rgba (1, 1, 0, 1); context.move_to (0, 0); context.rectangle (0, 0, width, height); context.stroke (); - var pattern = new Lib.Components.Pattern.linear ( + var linear_pattern = new Lib.Components.Pattern.linear ( Geometry.Point (0, height / 2.0), Geometry.Point (width, height / 2.0), false ); - pattern.colors = stop_colors; + linear_pattern.colors = pattern.colors; - var converted_pattern = Utils.Pattern.convert_to_cairo_pattern (pattern); + var converted_pattern = Utils.Pattern.convert_to_cairo_pattern (linear_pattern); context.set_source (converted_pattern); context.rectangle (0, 0, width, height); context.fill (); + } + + private void draw_stop_colors (Cairo.Context context) { + if (is_solid_pattern ()) { + return; + } + + context.set_source_rgba (0, 0, 0, 1); + context.set_line_width (STROKE_WIDTH); + + // First, draw the horizontal line over which all stop color nobs will be placed. + // Painting two strokes of contrasting colors makes it easier to spot the line. + context.move_to (0, height / 2.0 - STROKE_WIDTH); + context.line_to (width, height / 2.0 - STROKE_WIDTH); + context.stroke (); + context.set_source_rgba (1, 1, 1, 1); + + context.move_to (0, height / 2.0 + STROKE_WIDTH); + context.line_to (width, height / 2.0 + STROKE_WIDTH); + + context.stroke (); + + // Paint the stop colors. + foreach (var stop_color in pattern.colors) { + var position = stop_color.offset * width; + + if (selected_stop_color.offset == stop_color.offset) { + context.set_source_rgba (0.1568, 0.4745, 0.9823, 1); + } else { + context.set_source_rgba (0, 0, 0, 1); + } + + context.arc (position, height / 2.0, NOB_RADII, 0, 2 * Math.PI); + context.fill (); + + context.set_source_rgba (1, 1, 1, 1); + context.set_line_width (2 * STROKE_WIDTH); + context.arc (position, height / 2.0, NOB_RADII + 1, 0, 2 * Math.PI); + context.stroke (); + } + } + + private bool handle_button_press (Gdk.EventButton event) { + if (is_solid_pattern ()) { + return true; + } + + // Offset of current event expressed as fraction of width. + double event_offset = event.x / width; + + // Get the stop color at this location. + // If none exists, get ones that are just greater or smaller than it. + var left_stop_color = pattern.colors.floor (Lib.Components.Pattern.StopColor () {offset = event_offset}); + var right_stop_color = pattern.colors.ceil (Lib.Components.Pattern.StopColor () {offset = event_offset}); + + double left_offset_actual_value = (left_stop_color.offset - event_offset) * width; + double right_offset_actual_value = (right_stop_color.offset - event_offset) * width; + + // Check which one is within the threshold. + if (left_offset_actual_value.abs () < NOB_RADII) { + selected_stop_color = left_stop_color; + } else if (right_offset_actual_value.abs () < NOB_RADII) { + selected_stop_color = right_stop_color; + } else { + // If neither of the stop colors is anywhere near close, create a new one here. + var new_stop_color = Lib.Components.Pattern.StopColor () {offset = event_offset, color = left_stop_color.color}; + pattern.colors.add (new_stop_color); + selected_stop_color = new_stop_color; + } + + pattern_edited (pattern); + queue_draw (); + + return true; + } + + // As we used the BUTTON_MOTION_MASK, this method will be called only when mouse is clicked and dragged. + private bool handle_motion_notify (Gdk.EventMotion event) { + double event_offset = event.x / width; + + pattern.colors.remove (selected_stop_color); + selected_stop_color.offset = event_offset; + pattern.colors.add (selected_stop_color); + + pattern_edited (pattern); + queue_draw (); + + return true; + } + + private bool handle_button_release (Gdk.EventButton event) { return true; } + + private bool is_solid_pattern () { + if (pattern.colors.size == 1) { + // Single stop color means solid pattern. + return true; + } + + return false; + } + } diff --git a/src/Widgets/PatternTypeChooser.vala b/src/Widgets/PatternTypeChooser.vala index 9df79b3e..370b076d 100644 --- a/src/Widgets/PatternTypeChooser.vala +++ b/src/Widgets/PatternTypeChooser.vala @@ -28,7 +28,7 @@ public class Akira.Widgets.PatternTypeChooser : Granite.Widgets.ModeButton { private unowned Lib.ViewCanvas canvas; - // Trigger this signal when the active PatternType changes or gradient editor changes. + // Trigger this signal when the active PatternType changes. public signal void pattern_changed (Lib.Components.Pattern pattern); private Models.ColorModel model; From 95454869e4189b5807b90bbf3071e38c6d9ac977 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Mon, 25 Apr 2022 22:45:51 +0530 Subject: [PATCH 14/21] Modify gradient stop color using color chooser --- src/Drawables/Drawable.vala | 2 +- src/Lib/Components/Fills.vala | 4 ++-- src/Lib/Components/Pattern.vala | 4 ++-- src/Utils/Pattern.vala | 2 +- src/Widgets/ColorChooser.vala | 4 ++++ src/Widgets/GradientEditor.vala | 19 ++++++++++++++++--- 6 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/Drawables/Drawable.vala b/src/Drawables/Drawable.vala index 0800f3a4..12235b84 100644 --- a/src/Drawables/Drawable.vala +++ b/src/Drawables/Drawable.vala @@ -43,7 +43,7 @@ public class Akira.Drawables.Drawable { // Style public double line_width { get; set; default = 0; } - public Cairo.Pattern fill_pattern { get; set; default = new Cairo.Pattern.rgba (255, 255, 255, 255); } + public Cairo.Pattern fill_pattern { get; set; default = new Cairo.Pattern.rgba (1, 1, 1, 1); } public Gdk.RGBA stroke_rgba { get; set; default = Gdk.RGBA (); } public BorderType border_type { get; set; default = BorderType.CENTER; } public double radius_tr { get; set; default = 0; } diff --git a/src/Lib/Components/Fills.vala b/src/Lib/Components/Fills.vala index e7e9a2cf..c5ba6b8c 100644 --- a/src/Lib/Components/Fills.vala +++ b/src/Lib/Components/Fills.vala @@ -86,11 +86,11 @@ public class Akira.Lib.Components.Fills : Component, Copyable { _id = id; // active_pattern = Pattern.PatternType.SOLID; - // solid_pattern = new Pattern.solid (0, 0, 0, 255); + // solid_pattern = new Pattern.solid (0, 0, 0, 1); // linear_pattern = new Pattern.linear (Geometry.Point (0, 0), Geometry.Point (100, 100), false); // linear_pattern.add_stop_color (Gdk.RGBA () {red = 0, green = 0, blue = 0, alpha = 0}, 0); - // linear_pattern.add_stop_color (Gdk.RGBA () {red = 255, green = 255, blue = 255, alpha = 0}, 1); + // linear_pattern.add_stop_color (Gdk.RGBA () {red = 1, green = 1, blue = 1, alpha = 0}, 1); // radial_pattern = new Pattern.radial (); } diff --git a/src/Lib/Components/Pattern.vala b/src/Lib/Components/Pattern.vala index 959dea93..6e8359a2 100644 --- a/src/Lib/Components/Pattern.vala +++ b/src/Lib/Components/Pattern.vala @@ -59,8 +59,8 @@ public class Akira.Lib.Components.Pattern { // By default, all linear gradients will be created with black and white colors at start and end. colors = new Gee.TreeSet (are_equal); - colors.add (StopColor () {offset = 0, color = Gdk.RGBA () {red = 0, green = 0, blue = 0, alpha = 255}}); - colors.add (StopColor () {offset = 1, color = Gdk.RGBA () {red = 255, green = 255, blue = 255, alpha = 255}}); + colors.add (StopColor () {offset = 0, color = Gdk.RGBA () {red = 0, green = 0, blue = 0, alpha = 1}}); + colors.add (StopColor () {offset = 1, color = Gdk.RGBA () {red = 1, green = 1, blue = 1, alpha = 1}}); this.type = PatternType.LINEAR; this.hidden = hidden; diff --git a/src/Utils/Pattern.vala b/src/Utils/Pattern.vala index f215c19b..43bb5451 100644 --- a/src/Utils/Pattern.vala +++ b/src/Utils/Pattern.vala @@ -73,7 +73,7 @@ public class Akira.Utils.Pattern { } public static Cairo.Pattern default_pattern () { - return new Cairo.Pattern.rgba (255, 255, 255, 255); + return new Cairo.Pattern.rgba (1, 1, 1, 1); } // In Components.Pattern, the start and end positions of gradients are stored as diff --git a/src/Widgets/ColorChooser.vala b/src/Widgets/ColorChooser.vala index 6b192482..8d6d1f26 100644 --- a/src/Widgets/ColorChooser.vala +++ b/src/Widgets/ColorChooser.vala @@ -65,6 +65,10 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { }; chooser.notify["rgba"].connect (on_color_changed); + gradient_editor.color_changed.connect ((color) => { + chooser.rgba = color; + }); + attach (chooser, 0, 2, 1, 1); var global_label = new Gtk.Label (_("Global colors")) { diff --git a/src/Widgets/GradientEditor.vala b/src/Widgets/GradientEditor.vala index 7922daee..5665e731 100644 --- a/src/Widgets/GradientEditor.vala +++ b/src/Widgets/GradientEditor.vala @@ -54,6 +54,7 @@ public class Akira.Widgets.GradientEditor : Gtk.DrawingArea { hexpand = true; height_request = 40; margin = 5; + set_events ( Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK | @@ -69,6 +70,8 @@ public class Akira.Widgets.GradientEditor : Gtk.DrawingArea { draw.connect (draw_editor); }); + color_changed.connect (handle_color_changed); + this.button_press_event.connect (handle_button_press); this.motion_notify_event.connect (handle_motion_notify); this.button_release_event.connect (handle_button_release); @@ -139,7 +142,7 @@ public class Akira.Widgets.GradientEditor : Gtk.DrawingArea { context.arc (position, height / 2.0, NOB_RADII, 0, 2 * Math.PI); context.fill (); - + context.set_source_rgba (1, 1, 1, 1); context.set_line_width (2 * STROKE_WIDTH); context.arc (position, height / 2.0, NOB_RADII + 1, 0, 2 * Math.PI); @@ -147,6 +150,15 @@ public class Akira.Widgets.GradientEditor : Gtk.DrawingArea { } } + private void handle_color_changed (Gdk.RGBA color) { + pattern.colors.remove (selected_stop_color); + selected_stop_color.color = color; + pattern.colors.add (selected_stop_color); + + pattern_edited (pattern); + queue_draw (); + } + private bool handle_button_press (Gdk.EventButton event) { if (is_solid_pattern ()) { return true; @@ -174,7 +186,8 @@ public class Akira.Widgets.GradientEditor : Gtk.DrawingArea { pattern.colors.add (new_stop_color); selected_stop_color = new_stop_color; } - + + color_changed (selected_stop_color.color); pattern_edited (pattern); queue_draw (); @@ -207,5 +220,5 @@ public class Akira.Widgets.GradientEditor : Gtk.DrawingArea { return false; } - + } From 705c24e66524bc0c2aebb51fb435c35f62af105d Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Wed, 27 Apr 2022 19:58:43 +0530 Subject: [PATCH 15/21] Use gradients for border of canvas item: --- src/Drawables/Drawable.vala | 4 +- src/Layouts/BordersList/BorderItemModel.vala | 18 +++- src/Layouts/BordersList/BorderListItem.vala | 2 +- src/Lib/Components/Borders.vala | 95 +++++++++++++++++--- src/Lib/Components/CompiledBorder.vala | 43 ++++----- src/Lib/Items/ModelTypeEllipse.vala | 4 +- src/Lib/Items/ModelTypePath.vala | 4 +- src/Lib/Items/ModelTypeRect.vala | 4 +- src/Lib/Items/ModelTypeText.vala | 4 +- src/ViewLayers/ViewLayerMultiSelect.vala | 2 +- src/Widgets/PatternTypeChooser.vala | 6 ++ 11 files changed, 138 insertions(+), 48 deletions(-) diff --git a/src/Drawables/Drawable.vala b/src/Drawables/Drawable.vala index 12235b84..2f888db7 100644 --- a/src/Drawables/Drawable.vala +++ b/src/Drawables/Drawable.vala @@ -44,7 +44,7 @@ public class Akira.Drawables.Drawable { // Style public double line_width { get; set; default = 0; } public Cairo.Pattern fill_pattern { get; set; default = new Cairo.Pattern.rgba (1, 1, 1, 1); } - public Gdk.RGBA stroke_rgba { get; set; default = Gdk.RGBA (); } + public Cairo.Pattern border_pattern { get; set; default = new Cairo.Pattern.rgba (1, 1, 1, 1); } public BorderType border_type { get; set; default = BorderType.CENTER; } public double radius_tr { get; set; default = 0; } public double radius_tl { get; set; default = 0; } @@ -396,7 +396,7 @@ public class Akira.Drawables.Drawable { return true; } - context.set_source_rgba (stroke_rgba.red, stroke_rgba.green, stroke_rgba.blue, stroke_rgba.alpha); + context.set_source (border_pattern); context.set_line_width (line_width); context.set_antialias (Cairo.Antialias.GRAY); return line_width > 0; diff --git a/src/Layouts/BordersList/BorderItemModel.vala b/src/Layouts/BordersList/BorderItemModel.vala index 5a45d3cd..94126fdf 100644 --- a/src/Layouts/BordersList/BorderItemModel.vala +++ b/src/Layouts/BordersList/BorderItemModel.vala @@ -39,9 +39,16 @@ public class Akira.Layouts.BordersList.BorderItemModel : Models.ColorModel { var node = im.item_model.node_from_id (_cached_instance.id); assert (node != null); - var new_color = Lib.Components.Color.from_rgba (color, hidden); + // var new_color = Lib.Components.Color.from_rgba (color, hidden); + // var new_borders = node.instance.components.borders.copy (); + // new_borders.replace (Lib.Components.Borders.Border (border_id, new_color)); + // node.instance.components.borders = new_borders; + + print("Pattern type %s\n", pattern.type.to_string ()); + var new_pattern = pattern.copy (); var new_borders = node.instance.components.borders.copy (); - new_borders.replace (Lib.Components.Borders.Border (border_id, new_color)); + var new_border = new_borders.border_from_id (border_id).with_replaced_pattern (new_pattern); + new_borders.replace (new_border); node.instance.components.borders = new_borders; im.item_model.alert_node_changed (node, Lib.Components.Component.Type.COMPILED_BORDER); @@ -74,7 +81,12 @@ public class Akira.Layouts.BordersList.BorderItemModel : Models.ColorModel { (blocker); var border = _cached_instance.components.borders.border_from_id (border_id); - color = border.color; + active_pattern_type = border.active_pattern; + + solid_pattern = border.solid_pattern; + linear_pattern = border.linear_pattern; + radial_pattern = border.radial_pattern; + hidden = border.hidden; } } diff --git a/src/Layouts/BordersList/BorderListItem.vala b/src/Layouts/BordersList/BorderListItem.vala index da5b59d6..be8857fe 100644 --- a/src/Layouts/BordersList/BorderListItem.vala +++ b/src/Layouts/BordersList/BorderListItem.vala @@ -50,7 +50,7 @@ public class Akira.Layouts.BordersList.BorderListItem : VirtualizingListBoxRow { context.add_class ("selected-color-container"); context.add_class ("bg-pattern"); - color_button = new Widgets.ColorButton (); + color_button = new Widgets.ColorButton (view_canvas.window); container.add (color_button); eyedropper_button = new Widgets.EyeDropperButton () {}; diff --git a/src/Lib/Components/Borders.vala b/src/Lib/Components/Borders.vala index 2b8ef5c0..48c89f4e 100644 --- a/src/Lib/Components/Borders.vala +++ b/src/Lib/Components/Borders.vala @@ -22,25 +22,79 @@ public class Akira.Lib.Components.Borders : Component, Copyable { public struct Border { public int _id; - public Color _color; + public Pattern _pattern { + get { + switch (active_pattern) { + case Pattern.PatternType.SOLID: + return solid_pattern; + case Pattern.PatternType.LINEAR: + return linear_pattern; + case Pattern.PatternType.RADIAL: + return radial_pattern; + default: + return solid_pattern; + } + } + + set { + switch (active_pattern) { + case Pattern.PatternType.SOLID: + solid_pattern = value; + break; + case Pattern.PatternType.LINEAR: + linear_pattern = value; + break; + case Pattern.PatternType.RADIAL: + radial_pattern = value; + break; + default: + solid_pattern = value; + break; + } + } + } + + // Each border item will have patterns for all three types, + // so that user can easily switch between them. + // However, the non active patterns will not be serialized. + public Pattern solid_pattern; + public Pattern linear_pattern; + public Pattern radial_pattern; + + public Pattern.PatternType active_pattern; + public double _size; - public Border (int id = -1, Color color = Color (), double? size = null) { + public Border (int id = -1, Pattern pattern = new Pattern (), double? size = null) { _id = id; - _color = color; + active_pattern = Pattern.PatternType.SOLID; + + var border_rgba = Gdk.RGBA (); + border_rgba.parse (settings.border_color); + solid_pattern = new Pattern.solid (border_rgba, false); + + linear_pattern = new Pattern.linear (Geometry.Point (5, 5), Geometry.Point (95, 95), false); + radial_pattern = new Pattern.radial (); + _size = size != null ? size : settings.border_size; } public Border.deserialized (int id, Json.Object obj) { _id = id; - _color = Color.deserialized (obj.get_object_member ("color")); _size = (double)obj.get_int_member ("size"); } + public Border.with_all_patterns (int id, Pattern solid_pattern, Pattern linear_pattern, Pattern radial_pattern, Pattern.PatternType type) { + this.solid_pattern = solid_pattern; + this.linear_pattern = linear_pattern; + this.radial_pattern = radial_pattern; + this.active_pattern = type; + } + public Json.Node serialize () { var obj = new Json.Object (); obj.set_int_member ("id", _id); - obj.set_member ("color", _color.serialize ()); + obj.set_member ("pattern", _pattern.serialize ()); obj.set_double_member ("size", _size); var node = new Json.Node (Json.NodeType.OBJECT); node.set_object (obj); @@ -53,14 +107,14 @@ public class Akira.Lib.Components.Borders : Component, Copyable { return _id; } } - public Gdk.RGBA color { + public Pattern pattern { get { - return _color.rgba; + return _pattern; } } public bool hidden { get { - return _color.hidden; + return _pattern.hidden; } } public double size { @@ -71,11 +125,25 @@ public class Akira.Lib.Components.Borders : Component, Copyable { // Mutators. public Border with_color (Color new_color) { - return Border (_id, new_color, _size); + Pattern pattern = new Pattern.solid (new_color.rgba, new_color.hidden); + var border = Border (id, pattern, _size); + return border; + } + + public Border with_replaced_pattern (Pattern new_pattern) { + var new_border = Border (); + + new_border._id = this._id; + new_border.active_pattern = new_pattern.type; + new_border.solid_pattern = this.solid_pattern; + new_border.linear_pattern = this.linear_pattern; + new_border.radial_pattern = this.radial_pattern; + + return new_border; } public Border with_size (double new_size) { - return Border (_id, _color, new_size); + return Border (_id, _pattern, new_size); } } @@ -87,7 +155,7 @@ public class Akira.Lib.Components.Borders : Component, Copyable { public Borders.single_color (Color color, int size) { data = new Border[1]; - data[0] = Border (0, color, size); + data[0] = Border (0, new Pattern.solid (color.rgba, color.hidden), size); } public Borders.deserialized (Json.Object obj) { @@ -124,7 +192,7 @@ public class Akira.Lib.Components.Borders : Component, Copyable { public Border? border_from_id (int id) { foreach (unowned var border in data) { if (border.id == id) { - return border.with_color (border._color); + return Border.with_all_patterns (id, border.solid_pattern, border.linear_pattern, border.radial_pattern, border.active_pattern); } } return null; @@ -150,7 +218,8 @@ public class Akira.Lib.Components.Borders : Component, Copyable { latest_id = int.max (latest_id, border.id); } latest_id++; - var border = Border ((int) latest_id, color); + var pattern = new Pattern.solid (color.rgba, color.hidden); + var border = Border ((int) latest_id, pattern); data.resize (data.length + 1); data[data.length - 1] = border; } diff --git a/src/Lib/Components/CompiledBorder.vala b/src/Lib/Components/CompiledBorder.vala index 0e788faa..839dc9db 100644 --- a/src/Lib/Components/CompiledBorder.vala +++ b/src/Lib/Components/CompiledBorder.vala @@ -20,12 +20,12 @@ */ public class Akira.Lib.Components.CompiledBorder : Copyable { - private Gdk.RGBA _color; + private Pattern _pattern; private double _size; private bool _visible; - public Gdk.RGBA color { - get { return _color; } + public Pattern pattern { + get { return _pattern; } } public double size { @@ -36,42 +36,41 @@ public class Akira.Lib.Components.CompiledBorder : Copyable { get { return _visible; } } - public CompiledBorder (Gdk.RGBA color, double size, bool visible) { - _color = color; + public CompiledBorder (Pattern pattern, double size, bool visible) { + _pattern = pattern; _size = size; _visible = visible; } public CompiledBorder.as_empty () { - _color = Gdk.RGBA () { red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0}; + _pattern = new Pattern.solid (Gdk.RGBA () {red = 0, green = 0, blue = 0, alpha = 0}, false); _size = 0; _visible = false; } public CompiledBorder copy () { - return new CompiledBorder (_color, _size, _visible); + return new CompiledBorder (_pattern, size, _visible); } public static CompiledBorder compile (Components? components, Lib.Items.ModelNode? node) { - var rgba_border = Gdk.RGBA (); + var pattern_border = new Pattern (); bool has_colors = false; - double size = 0; + double border_size = 0; if (components == null) { - return new CompiledBorder (rgba_border, size, has_colors); + return new CompiledBorder (pattern_border, border_size, has_colors); } unowned var borders = components.borders; unowned var opacity = components.opacity; - - // Set an initial arbitrary color with full transparency. - rgba_border.alpha = 0; + unowned var size = components.size; + unowned var center = components.center; if (borders == null) { - return new CompiledBorder (rgba_border, size, false); + return new CompiledBorder (pattern_border, border_size, has_colors); } - // Loop through all the configured borders and reload the color. + // Loop through all the configured borders. for (var i = 0; i < borders.data.length; ++i) { // Skip if the border is hidden as we don't need to blend colors. if (borders.data[i].hidden) { @@ -79,17 +78,21 @@ public class Akira.Lib.Components.CompiledBorder : Copyable { } // Set the new blended color. - rgba_border = Utils.Color.blend_colors (rgba_border, borders.data[i].color); - size = double.max (size, borders.data[i].size); + // rgba_border = Utils.Color.blend_colors (rgba_border, borders.data[i].pattern.get_first_color ()); + pattern_border = Utils.Pattern.create_pattern_with_converted_positions (borders.data[i].pattern, size, center); has_colors = true; + + // TODO: Temporarily disable blending patterns. Not implemented. + break; } // Apply the mixed RGBA value only if we had one. if (has_colors && opacity != null) { - // Keep in consideration the global opacity to properly update the border color. - rgba_border.alpha = rgba_border.alpha * opacity.opacity / 100; + // Keep in consideration the global opacity to properly update the fill color. + // TODO: Disable this too. + // rgba_border.alpha = rgba_border.alpha * opacity.opacity / 100; } - return new CompiledBorder (rgba_border, size, has_colors && size != 0); + return new CompiledBorder (pattern_border, border_size, has_colors); } } diff --git a/src/Lib/Items/ModelTypeEllipse.vala b/src/Lib/Items/ModelTypeEllipse.vala index 5abaefef..b76247d4 100644 --- a/src/Lib/Items/ModelTypeEllipse.vala +++ b/src/Lib/Items/ModelTypeEllipse.vala @@ -61,14 +61,14 @@ public class Akira.Lib.Items.ModelTypeEllipse : ModelType { case Lib.Components.Component.Type.COMPILED_BORDER: if (!instance.compiled_border.is_visible) { instance.drawable.line_width = 0; - instance.drawable.stroke_rgba = Gdk.RGBA () { alpha = 0 }; + instance.drawable.border_pattern = Utils.Pattern.default_pattern (); break; } // The "line-width" property expects a DOUBLE type, but we don't support subpixels // so we always handle the border size as INT, therefore we need to type cast it here. instance.drawable.line_width = (double) instance.compiled_border.size; - instance.drawable.stroke_rgba = instance.compiled_border.color; + instance.drawable.border_pattern = Utils.Pattern.convert_to_cairo_pattern (instance.compiled_border.pattern); break; case Lib.Components.Component.Type.COMPILED_FILL: if (!instance.compiled_fill.is_visible) { diff --git a/src/Lib/Items/ModelTypePath.vala b/src/Lib/Items/ModelTypePath.vala index 9bee369b..aa34516a 100644 --- a/src/Lib/Items/ModelTypePath.vala +++ b/src/Lib/Items/ModelTypePath.vala @@ -71,14 +71,14 @@ public class Akira.Lib.Items.ModelTypePath : ModelType { case Lib.Components.Component.Type.COMPILED_BORDER: if (!instance.compiled_border.is_visible) { instance.drawable.line_width = 0; - instance.drawable.stroke_rgba = Gdk.RGBA () { alpha = 0 }; + instance.drawable.border_pattern = Utils.Pattern.default_pattern (); break; } // The "line-width" property expects a DOUBLE type, but we don't support subpixels // so we always handle the border size as INT, therefore we need to type cast it here. instance.drawable.line_width = (double) instance.compiled_border.size; - instance.drawable.stroke_rgba = instance.compiled_border.color; + instance.drawable.border_pattern = Utils.Pattern.convert_to_cairo_pattern (instance.compiled_border.pattern); break; case Lib.Components.Component.Type.COMPILED_FILL: if (!instance.compiled_fill.is_visible) { diff --git a/src/Lib/Items/ModelTypeRect.vala b/src/Lib/Items/ModelTypeRect.vala index 7110d4cf..720af761 100644 --- a/src/Lib/Items/ModelTypeRect.vala +++ b/src/Lib/Items/ModelTypeRect.vala @@ -66,14 +66,14 @@ public class Akira.Lib.Items.ModelTypeRect : ModelType { case Lib.Components.Component.Type.COMPILED_BORDER: if (!instance.compiled_border.is_visible) { instance.drawable.line_width = 0; - instance.drawable.stroke_rgba = Gdk.RGBA () { alpha = 0 }; + instance.drawable.border_pattern = Utils.Pattern.default_pattern (); break; } // The "line-width" property expects a DOUBLE type, but we don't support subpixels // so we always handle the border size as INT, therefore we need to type cast it here. instance.drawable.line_width = (double) instance.compiled_border.size; - instance.drawable.stroke_rgba = instance.compiled_border.color; + instance.drawable.border_pattern = Utils.Pattern.convert_to_cairo_pattern (instance.compiled_border.pattern); break; case Lib.Components.Component.Type.COMPILED_FILL: if (!instance.compiled_fill.is_visible) { diff --git a/src/Lib/Items/ModelTypeText.vala b/src/Lib/Items/ModelTypeText.vala index a61a8d10..e2ed429a 100644 --- a/src/Lib/Items/ModelTypeText.vala +++ b/src/Lib/Items/ModelTypeText.vala @@ -65,14 +65,14 @@ public class Akira.Lib.Items.ModelTypeText : ModelType { case Lib.Components.Component.Type.COMPILED_BORDER: if (!instance.compiled_border.is_visible) { instance.drawable.line_width = 0; - instance.drawable.stroke_rgba = Gdk.RGBA () { alpha = 0 }; + instance.drawable.border_pattern = Utils.Pattern.default_pattern (); break; } // The "line-width" property expects a DOUBLE type, but we don't support subpixels // so we always handle the border size as INT, therefore we need to type cast it here. instance.drawable.line_width = (double) instance.compiled_border.size; - instance.drawable.stroke_rgba = instance.compiled_border.color; + instance.drawable.border_pattern = Utils.Pattern.convert_to_cairo_pattern (instance.compiled_border.pattern); break; case Lib.Components.Component.Type.COMPILED_FILL: if (!instance.compiled_fill.is_visible) { diff --git a/src/ViewLayers/ViewLayerMultiSelect.vala b/src/ViewLayers/ViewLayerMultiSelect.vala index a9a6f354..a0090f6b 100644 --- a/src/ViewLayers/ViewLayerMultiSelect.vala +++ b/src/ViewLayers/ViewLayerMultiSelect.vala @@ -96,7 +96,7 @@ public class Akira.ViewLayers.ViewLayerMultiSelect : ViewLayer { drawable.fill_pattern = Utils.Pattern.convert_to_cairo_pattern (fill_pattern); drawable.line_width = UI_LINE_WIDTH / scale; - drawable.stroke_rgba = stroke; + drawable.border_pattern = Utils.Pattern.convert_to_cairo_pattern (new Lib.Components.Pattern.solid (stroke, false)); drawable.paint (context, target_bounds, scale, Drawables.Drawable.DrawType.NORMAL); last_drawn_bb = drawable.bounds; diff --git a/src/Widgets/PatternTypeChooser.vala b/src/Widgets/PatternTypeChooser.vala index 370b076d..46e117a9 100644 --- a/src/Widgets/PatternTypeChooser.vala +++ b/src/Widgets/PatternTypeChooser.vala @@ -41,12 +41,18 @@ public class Akira.Widgets.PatternTypeChooser : Granite.Widgets.ModeButton { // Connect signals. this.mode_changed.connect ((window) => { + print("Mode changed\n"); var active_mode = (Lib.Components.Pattern.PatternType) this.selected; + print("Got active mode as %s\n", active_mode.to_string ()); + print("Active pattern type is %s\n", model.active_pattern_type.to_string ()); model.active_pattern_type = active_mode; + print("Change active mode to %s\n", active_mode.to_string ()); pattern_changed (model.pattern); + print("Done signal\n"); handle_pattern_changed (); + print("Handled signal\n"); }); this.canvas = window.main_window.main_view_canvas.canvas; From 658c1ea1d8578ac11786ae33b55df3df2d844330 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Wed, 27 Apr 2022 21:57:39 +0530 Subject: [PATCH 16/21] Prevent fill pattern from changing when border pattern modified. --- src/Layouts/BordersList/BorderItemModel.vala | 8 +---- src/Layouts/FillsList/FillItemModel.vala | 35 +------------------- src/Lib/Components/CompiledBorder.vala | 1 + src/Models/ColorModel.vala | 34 +++++++++++++++++++ src/Widgets/PatternTypeChooser.vala | 15 ++++----- 5 files changed, 44 insertions(+), 49 deletions(-) diff --git a/src/Layouts/BordersList/BorderItemModel.vala b/src/Layouts/BordersList/BorderItemModel.vala index 94126fdf..c2a0eb6a 100644 --- a/src/Layouts/BordersList/BorderItemModel.vala +++ b/src/Layouts/BordersList/BorderItemModel.vala @@ -26,7 +26,7 @@ public class Akira.Layouts.BordersList.BorderItemModel : Models.ColorModel { private unowned Lib.ViewCanvas _view_canvas; - private Lib.Items.ModelInstance _cached_instance; + // private Lib.Items.ModelInstance _cached_instance; public int border_id; @@ -39,12 +39,6 @@ public class Akira.Layouts.BordersList.BorderItemModel : Models.ColorModel { var node = im.item_model.node_from_id (_cached_instance.id); assert (node != null); - // var new_color = Lib.Components.Color.from_rgba (color, hidden); - // var new_borders = node.instance.components.borders.copy (); - // new_borders.replace (Lib.Components.Borders.Border (border_id, new_color)); - // node.instance.components.borders = new_borders; - - print("Pattern type %s\n", pattern.type.to_string ()); var new_pattern = pattern.copy (); var new_borders = node.instance.components.borders.copy (); var new_border = new_borders.border_from_id (border_id).with_replaced_pattern (new_pattern); diff --git a/src/Layouts/FillsList/FillItemModel.vala b/src/Layouts/FillsList/FillItemModel.vala index ef479db3..79a6d0f8 100644 --- a/src/Layouts/FillsList/FillItemModel.vala +++ b/src/Layouts/FillsList/FillItemModel.vala @@ -26,7 +26,7 @@ public class Akira.Layouts.FillsList.FillItemModel : Models.ColorModel { private unowned Akira.Lib.ViewCanvas _view_canvas; - private Lib.Items.ModelInstance _cached_instance; + // private Lib.Items.ModelInstance _cached_instance; public int fill_id; @@ -62,39 +62,6 @@ public class Akira.Layouts.FillsList.FillItemModel : Models.ColorModel { im.compile_model (); } - public void move_pattern_position_by_delta (Utils.Nobs.Nob nob, Geometry.Point delta) { - Geometry.Point percent_delta = Geometry.Point ( - delta.x * 100.0 / _cached_instance.components.size.width, - delta.y * 100.0 / _cached_instance.components.size.height - ); - - switch (nob) { - case Utils.Nobs.Nob.GRADIENT_START: - pattern.start = Geometry.Point ( - pattern.start.x - percent_delta.x, - pattern.start.y - percent_delta.y - ); - break; - case Utils.Nobs.Nob.GRADIENT_END: - pattern.end = Geometry.Point ( - pattern.end.x - percent_delta.x, - pattern.end.y - percent_delta.y - ); - break; - case Utils.Nobs.Nob.GRADIENT_RADIUS_START: - pattern.radius_start = pattern.radius_start + percent_delta.x; - break; - case Utils.Nobs.Nob.GRADIENT_RADIUS_END: - pattern.radius_end = pattern.radius_end + percent_delta.x; - break; - default: - break; - } - - on_value_changed (); - value_changed (); - } - public FillItemModel (Lib.ViewCanvas view_canvas, Lib.Items.ModelNode node, int fill_id) { update_node (node, fill_id); _view_canvas = view_canvas; diff --git a/src/Lib/Components/CompiledBorder.vala b/src/Lib/Components/CompiledBorder.vala index 839dc9db..7a5200a7 100644 --- a/src/Lib/Components/CompiledBorder.vala +++ b/src/Lib/Components/CompiledBorder.vala @@ -79,6 +79,7 @@ public class Akira.Lib.Components.CompiledBorder : Copyable { // Set the new blended color. // rgba_border = Utils.Color.blend_colors (rgba_border, borders.data[i].pattern.get_first_color ()); + border_size = borders.data[i].size; pattern_border = Utils.Pattern.create_pattern_with_converted_positions (borders.data[i].pattern, size, center); has_colors = true; diff --git a/src/Models/ColorModel.vala b/src/Models/ColorModel.vala index 096d263d..fabe3974 100644 --- a/src/Models/ColorModel.vala +++ b/src/Models/ColorModel.vala @@ -40,6 +40,7 @@ public class Akira.Models.ColorModel : GLib.Object { } protected int block_signal = 0; + protected Lib.Items.ModelInstance _cached_instance; // All three types of patterns will be stored here. // Based on which type is active, update it. @@ -127,6 +128,39 @@ public class Akira.Models.ColorModel : GLib.Object { } } + public void move_pattern_position_by_delta (Utils.Nobs.Nob nob, Geometry.Point delta) { + Geometry.Point percent_delta = Geometry.Point ( + delta.x * 100.0 / _cached_instance.components.size.width, + delta.y * 100.0 / _cached_instance.components.size.height + ); + + switch (nob) { + case Utils.Nobs.Nob.GRADIENT_START: + pattern.start = Geometry.Point ( + pattern.start.x - percent_delta.x, + pattern.start.y - percent_delta.y + ); + break; + case Utils.Nobs.Nob.GRADIENT_END: + pattern.end = Geometry.Point ( + pattern.end.x - percent_delta.x, + pattern.end.y - percent_delta.y + ); + break; + case Utils.Nobs.Nob.GRADIENT_RADIUS_START: + pattern.radius_start = pattern.radius_start + percent_delta.x; + break; + case Utils.Nobs.Nob.GRADIENT_RADIUS_END: + pattern.radius_end = pattern.radius_end + percent_delta.x; + break; + default: + break; + } + + on_value_changed (); + value_changed (); + } + public virtual void on_value_changed () {} public virtual void delete () {} } diff --git a/src/Widgets/PatternTypeChooser.vala b/src/Widgets/PatternTypeChooser.vala index 46e117a9..306a8ee5 100644 --- a/src/Widgets/PatternTypeChooser.vala +++ b/src/Widgets/PatternTypeChooser.vala @@ -41,25 +41,24 @@ public class Akira.Widgets.PatternTypeChooser : Granite.Widgets.ModeButton { // Connect signals. this.mode_changed.connect ((window) => { - print("Mode changed\n"); var active_mode = (Lib.Components.Pattern.PatternType) this.selected; - print("Got active mode as %s\n", active_mode.to_string ()); - print("Active pattern type is %s\n", model.active_pattern_type.to_string ()); model.active_pattern_type = active_mode; - print("Change active mode to %s\n", active_mode.to_string ()); pattern_changed (model.pattern); - print("Done signal\n"); - + handle_pattern_changed (); - print("Handled signal\n"); }); this.canvas = window.main_window.main_view_canvas.canvas; window.event_bus.translate_gradient_nob_by_delta.connect ((nob, delta) => { - ((Layouts.FillsList.FillItemModel) model).move_pattern_position_by_delta (nob, delta); + // This condition checks if this widget is currently open as there can be multiple instances + // if PatternTypeChooser present. And we don't want all of them to handle this signal. + if (!is_drawable ()) { + return; + } + model.move_pattern_position_by_delta (nob, delta); handle_pattern_changed (); }); } From 5d52c2283e2f8f32743fc8dacb33cc898468af55 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Wed, 27 Apr 2022 22:30:22 +0530 Subject: [PATCH 17/21] Implement radial gradient --- src/Lib/Components/Borders.vala | 2 +- src/Lib/Components/Fills.vala | 2 +- src/Lib/Components/Pattern.vala | 18 +++++++++++------- src/Lib/Managers/NobManager.vala | 5 ----- src/Lib/Modes/TransformMode.vala | 2 -- src/Models/ColorModel.vala | 6 ------ src/Utils/Nobs.vala | 12 +----------- src/Utils/Pattern.vala | 9 +++++---- src/ViewLayers/ViewLayerNobs.vala | 15 +-------------- src/Widgets/PatternTypeChooser.vala | 4 +--- 10 files changed, 21 insertions(+), 54 deletions(-) diff --git a/src/Lib/Components/Borders.vala b/src/Lib/Components/Borders.vala index 48c89f4e..6c2d1dcd 100644 --- a/src/Lib/Components/Borders.vala +++ b/src/Lib/Components/Borders.vala @@ -74,7 +74,7 @@ public class Akira.Lib.Components.Borders : Component, Copyable { solid_pattern = new Pattern.solid (border_rgba, false); linear_pattern = new Pattern.linear (Geometry.Point (5, 5), Geometry.Point (95, 95), false); - radial_pattern = new Pattern.radial (); + radial_pattern = new Pattern.radial (Geometry.Point (5, 5), Geometry.Point (95, 95), false); _size = size != null ? size : settings.border_size; } diff --git a/src/Lib/Components/Fills.vala b/src/Lib/Components/Fills.vala index bdcea200..81d022c0 100644 --- a/src/Lib/Components/Fills.vala +++ b/src/Lib/Components/Fills.vala @@ -72,7 +72,7 @@ public class Akira.Lib.Components.Fills : Component, Copyable { solid_pattern = new Pattern.solid (fill_rgba, false); linear_pattern = new Pattern.linear (Geometry.Point (5, 5), Geometry.Point (95, 95), false); - radial_pattern = new Pattern.radial (); + radial_pattern = new Pattern.radial (Geometry.Point (5, 5), Geometry.Point (95, 95), false); } public Fill.with_all_patterns (int id, Pattern solid_pattern, Pattern linear_pattern, Pattern radial_pattern, Pattern.PatternType type) { diff --git a/src/Lib/Components/Pattern.vala b/src/Lib/Components/Pattern.vala index 6e8359a2..45df040b 100644 --- a/src/Lib/Components/Pattern.vala +++ b/src/Lib/Components/Pattern.vala @@ -41,9 +41,6 @@ public class Akira.Lib.Components.Pattern { // The values are relative to the canvas item's position. public Geometry.Point start; public Geometry.Point end; - // These values are only used for radial gradients to denote radii. - public double radius_start; - public double radius_end; public Pattern.solid (Gdk.RGBA color, bool hidden) { this.type = PatternType.SOLID; @@ -66,8 +63,17 @@ public class Akira.Lib.Components.Pattern { this.hidden = hidden; } - public Pattern.radial () { - // TODO: + public Pattern.radial (Geometry.Point start, Geometry.Point end, bool hidden) { + this.start = start; + this.end = end; + + // By default, all linear gradients will be created with black and white colors at start and end. + colors = new Gee.TreeSet (are_equal); + colors.add (StopColor () {offset = 0, color = Gdk.RGBA () {red = 0, green = 0, blue = 0, alpha = 1}}); + colors.add (StopColor () {offset = 1, color = Gdk.RGBA () {red = 1, green = 1, blue = 1, alpha = 1}}); + + this.type = PatternType.RADIAL; + this.hidden = hidden; } public Pattern copy () { @@ -78,8 +84,6 @@ public class Akira.Lib.Components.Pattern { new_pattern.hidden = this.hidden; new_pattern.start = this.start; new_pattern.end = this.end; - new_pattern.radius_start = this.radius_start; - new_pattern.radius_end = this.radius_end; return new_pattern; } diff --git a/src/Lib/Managers/NobManager.vala b/src/Lib/Managers/NobManager.vala index 917d6d6b..bc04fcbe 100644 --- a/src/Lib/Managers/NobManager.vala +++ b/src/Lib/Managers/NobManager.vala @@ -42,10 +42,8 @@ public class Akira.Lib.Managers.NobManager : Object { view_canvas.window.event_bus.change_gradient_nobs_visibility.connect ((visible) => { if (visible) { nob_layer.render_gradient_nobs = true; - nob_layer.render_gradient_radii_nobs = true; } else { nob_layer.render_gradient_nobs = false; - nob_layer.render_gradient_radii_nobs = false; } update_nob_layer (); @@ -182,13 +180,10 @@ public class Akira.Lib.Managers.NobManager : Object { public void set_layer_flags_from_pattern_type (Lib.Components.Pattern.PatternType ptype) { if (ptype == Lib.Components.Pattern.PatternType.SOLID) { nob_layer.render_gradient_nobs = false; - nob_layer.render_gradient_radii_nobs = false; } else if (ptype == Lib.Components.Pattern.PatternType.LINEAR) { nob_layer.render_gradient_nobs = true; - nob_layer.render_gradient_radii_nobs = false; } else if (ptype == Lib.Components.Pattern.PatternType.RADIAL) { nob_layer.render_gradient_nobs = true; - nob_layer.render_gradient_radii_nobs = true; } nob_layer.update_nob_data (nobs); diff --git a/src/Lib/Modes/TransformMode.vala b/src/Lib/Modes/TransformMode.vala index 090629a6..20deacc9 100644 --- a/src/Lib/Modes/TransformMode.vala +++ b/src/Lib/Modes/TransformMode.vala @@ -182,8 +182,6 @@ public class Akira.Lib.Modes.TransformMode : AbstractInteractionMode { break; case Utils.Nobs.Nob.GRADIENT_START: case Utils.Nobs.Nob.GRADIENT_END: - case Utils.Nobs.Nob.GRADIENT_RADIUS_START: - case Utils.Nobs.Nob.GRADIENT_RADIUS_END: move_gradient_nob ( view_canvas, nob, diff --git a/src/Models/ColorModel.vala b/src/Models/ColorModel.vala index fabe3974..cfb61589 100644 --- a/src/Models/ColorModel.vala +++ b/src/Models/ColorModel.vala @@ -147,12 +147,6 @@ public class Akira.Models.ColorModel : GLib.Object { pattern.end.y - percent_delta.y ); break; - case Utils.Nobs.Nob.GRADIENT_RADIUS_START: - pattern.radius_start = pattern.radius_start + percent_delta.x; - break; - case Utils.Nobs.Nob.GRADIENT_RADIUS_END: - pattern.radius_end = pattern.radius_end + percent_delta.x; - break; default: break; } diff --git a/src/Utils/Nobs.vala b/src/Utils/Nobs.vala index e59157a6..6b3fb9e7 100644 --- a/src/Utils/Nobs.vala +++ b/src/Utils/Nobs.vala @@ -45,8 +45,6 @@ public class Akira.Utils.Nobs : Object { ROTATE, GRADIENT_START, GRADIENT_END, - GRADIENT_RADIUS_START, - GRADIENT_RADIUS_END, ALL } @@ -176,9 +174,7 @@ public class Akira.Utils.Nobs : Object { public static bool is_gradient_nob (Nob nob) { return ( nob == Nob.GRADIENT_START || - nob == Nob.GRADIENT_END || - nob == Nob.GRADIENT_RADIUS_START || - nob == Nob.GRADIENT_RADIUS_END + nob == Nob.GRADIENT_END ); } @@ -224,12 +220,6 @@ public class Akira.Utils.Nobs : Object { case Nob.GRADIENT_END: result = Gdk.CursorType.TCROSS; break; - case Nob.GRADIENT_RADIUS_START: - result = Gdk.CursorType.TCROSS; - break; - case Nob.GRADIENT_RADIUS_END: - result = Gdk.CursorType.TCROSS; - break; default: break; } diff --git a/src/Utils/Pattern.vala b/src/Utils/Pattern.vala index 43bb5451..ab098a9c 100644 --- a/src/Utils/Pattern.vala +++ b/src/Utils/Pattern.vala @@ -43,13 +43,14 @@ public class Akira.Utils.Pattern { } break; case Lib.Components.Pattern.PatternType.RADIAL: + double distance = Utils.GeometryMath.distance (pattern.start.x, pattern.start.y, pattern.end.x, pattern.end.y); converted = new Cairo.Pattern.radial ( pattern.start.x, pattern.start.y, - pattern.radius_start, - pattern.end.x, - pattern.end.y, - pattern.radius_end + 0, + pattern.start.x, + pattern.start.y, + distance ); foreach (var stop_color in pattern.colors) { diff --git a/src/ViewLayers/ViewLayerNobs.vala b/src/ViewLayers/ViewLayerNobs.vala index b83dcb99..efa0ba35 100644 --- a/src/ViewLayers/ViewLayerNobs.vala +++ b/src/ViewLayers/ViewLayerNobs.vala @@ -35,8 +35,6 @@ public class Akira.ViewLayers.ViewLayerNobs : ViewLayer { // If this is true, draw the start and end nobs for linear and radial gradients. public bool render_gradient_nobs = false; - // If this is true, draw the radius nobs. Only use for radial gradients. - public bool render_gradient_radii_nobs = false; public void update_nob_data (Utils.Nobs.NobSet? new_nobs) { if (nobs != null) { @@ -118,15 +116,6 @@ public class Akira.ViewLayers.ViewLayerNobs : ViewLayer { } } - if ( - nob.handle_id == Utils.Nobs.Nob.GRADIENT_RADIUS_START || - nob.handle_id == Utils.Nobs.Nob.GRADIENT_RADIUS_END - ) { - if (!render_gradient_radii_nobs) { - continue; - } - } - context.save (); context.new_path (); @@ -137,9 +126,7 @@ public class Akira.ViewLayers.ViewLayerNobs : ViewLayer { if ( nob.handle_id == Utils.Nobs.Nob.ROTATE || nob.handle_id == Utils.Nobs.Nob.GRADIENT_START || - nob.handle_id == Utils.Nobs.Nob.GRADIENT_END || - nob.handle_id == Utils.Nobs.Nob.GRADIENT_RADIUS_START || - nob.handle_id == Utils.Nobs.Nob.GRADIENT_RADIUS_END + nob.handle_id == Utils.Nobs.Nob.GRADIENT_END ) { context.arc (0, 0, radius, 0, 2.0 * GLib.Math.PI); } else { diff --git a/src/Widgets/PatternTypeChooser.vala b/src/Widgets/PatternTypeChooser.vala index 306a8ee5..98821c19 100644 --- a/src/Widgets/PatternTypeChooser.vala +++ b/src/Widgets/PatternTypeChooser.vala @@ -45,7 +45,7 @@ public class Akira.Widgets.PatternTypeChooser : Granite.Widgets.ModeButton { model.active_pattern_type = active_mode; pattern_changed (model.pattern); - + handle_pattern_changed (); }); @@ -94,8 +94,6 @@ public class Akira.Widgets.PatternTypeChooser : Granite.Widgets.ModeButton { canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_START, hidden_pos); canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_END, hidden_pos); - canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_RADIUS_START, hidden_pos); - canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_RADIUS_END, hidden_pos); if (active_mode == Lib.Components.Pattern.PatternType.LINEAR) { canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_START, start_nob_pos); From d42cb089de446d4a3b96feb7c8eebdef9cc3c0f4 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Sat, 30 Apr 2022 21:36:46 +0530 Subject: [PATCH 18/21] Save and load patterns from global colors --- src/Lib/Components/Fills.vala | 10 ++---- src/Lib/Components/Pattern.vala | 54 ++++++++++++++++++++++++----- src/Widgets/ColorChooser.vala | 37 +++++++++++++------- src/Widgets/RoundedColorButton.vala | 15 ++++---- 4 files changed, 82 insertions(+), 34 deletions(-) diff --git a/src/Lib/Components/Fills.vala b/src/Lib/Components/Fills.vala index 81d022c0..62cab222 100644 --- a/src/Lib/Components/Fills.vala +++ b/src/Lib/Components/Fills.vala @@ -76,6 +76,7 @@ public class Akira.Lib.Components.Fills : Component, Copyable { } public Fill.with_all_patterns (int id, Pattern solid_pattern, Pattern linear_pattern, Pattern radial_pattern, Pattern.PatternType type) { + this._id = id; this.solid_pattern = solid_pattern; this.linear_pattern = linear_pattern; this.radial_pattern = radial_pattern; @@ -121,13 +122,8 @@ public class Akira.Lib.Components.Fills : Component, Copyable { } public Fill with_replaced_pattern (Pattern new_pattern) { - var new_fill = Fill (); - - new_fill._id = this._id; - new_fill.active_pattern = new_pattern.type; - new_fill.solid_pattern = this.solid_pattern; - new_fill.linear_pattern = this.linear_pattern; - new_fill.radial_pattern = this.radial_pattern; + var new_fill = Fill.with_all_patterns (_id, solid_pattern, linear_pattern, radial_pattern, new_pattern.type); + new_fill._pattern = new_pattern; return new_fill; } diff --git a/src/Lib/Components/Pattern.vala b/src/Lib/Components/Pattern.vala index 45df040b..784b55f7 100644 --- a/src/Lib/Components/Pattern.vala +++ b/src/Lib/Components/Pattern.vala @@ -31,6 +31,30 @@ public class Akira.Lib.Components.Pattern { // Value between 0 and 1. Represents what distance the stop color is located at. public double offset; public Gdk.RGBA color; + + public StopColor.deserialized (Json.Object obj) { + offset = obj.get_double_member ("offset"); + + color = Gdk.RGBA (); + color.red = obj.get_double_member ("red"); + color.green = obj.get_double_member ("green"); + color.blue = obj.get_double_member ("blue"); + color.alpha = obj.get_double_member ("alpha"); + } + + public Json.Node serialize () { + var obj = new Json.Object (); + + obj.set_double_member ("offset", offset); + obj.set_double_member ("red", color.red); + obj.set_double_member ("green", color.green); + obj.set_double_member ("blue", color.blue); + obj.set_double_member ("alpha", color.alpha); + + var node = new Json.Node (Json.NodeType.OBJECT); + node.set_object (obj); + return node; + } } public PatternType type; @@ -112,18 +136,32 @@ public class Akira.Lib.Components.Pattern { } public Pattern.deserialized (Json.Object obj) { - // TODO: - hidden = obj.get_boolean_member ("hidden"); + this.start = Geometry.Point.deserialized (obj.get_object_member ("start")); + this.end = Geometry.Point.deserialized (obj.get_object_member ("end")); + this.type = (PatternType) obj.get_int_member ("type"); + this.hidden = obj.get_boolean_member ("hidden"); + + var color_array = obj.get_array_member ("colors"); + this.colors = new Gee.TreeSet (are_equal); + foreach (var color_item in color_array.get_elements ()) { + colors.add (StopColor.deserialized (color_item.get_object ())); + } } public Json.Node serialize () { - // TODO: var obj = new Json.Object (); - // obj.set_double_member ("r", rgba.red); - // obj.set_double_member ("g", rgba.green); - // obj.set_double_member ("b", rgba.blue); - // obj.set_double_member ("a", rgba.alpha); - // obj.set_boolean_member ("hidden", hidden); + obj.set_member ("start", start.serialize ()); + obj.set_member ("end", end.serialize ()); + obj.set_int_member ("type", (int) type); + obj.set_boolean_member ("hidden", hidden); + + var color_array = new Json.Array (); + foreach (var color in colors) { + color_array.add_element (color.serialize ()); + } + + obj.set_array_member ("colors", color_array); + var node = new Json.Node (Json.NodeType.OBJECT); node.set_object (obj); return node; diff --git a/src/Widgets/ColorChooser.vala b/src/Widgets/ColorChooser.vala index 8d6d1f26..4b3c39bc 100644 --- a/src/Widgets/ColorChooser.vala +++ b/src/Widgets/ColorChooser.vala @@ -30,6 +30,8 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { private GradientEditor gradient_editor; private PatternTypeChooser pattern_chooser; + private Models.ColorModel model; + /* * Type of color containers to add new colors to. We can potentially create * an API to allow adding more containers to the color picker popup. @@ -44,6 +46,7 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { margin_start = margin_end = 3; row_spacing = 12; get_style_context ().add_class ("color-picker"); + this.model = model; pattern_chooser = new PatternTypeChooser (model, window); attach (pattern_chooser, 0, 0, 1, 1); @@ -95,8 +98,18 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { global_flowbox.add (add_global_color_btn); foreach (string color in settings.global_colors) { - var btn = create_color_button (color); - global_flowbox.add (btn); + var parser = new Json.Parser (); + try { + parser.load_from_data (color); + var node = parser.get_root (); + + var pattern = new Lib.Components.Pattern.deserialized (node.get_object ()); + + var btn = create_color_button (pattern); + global_flowbox.add (btn); + } catch (Error e) { + warning ("Unable to parse pattern. %s\n", e.message); + } } attach (global_flowbox, 0, 4, 1, 1); @@ -107,18 +120,18 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { * Add the current color to the parent flowbox. */ private void on_save_color (Container parent) { - // Get the currently active color. - var color = chooser.rgba.to_string (); + // Get the currently active pattern. + var pattern = model.pattern; // Create the new color button and connect to its signal. - var btn = create_color_button (color); + var btn = create_color_button (pattern); // Update the colors list and the schema based on the colors container. switch (parent) { case Container.GLOBAL: global_flowbox.add (btn); var array = settings.global_colors; - array += color; + array += Json.to_string (pattern.serialize (), false); settings.global_colors = array; break; @@ -128,16 +141,16 @@ public class Akira.Widgets.ColorChooser : Gtk.Grid { } } - private Gtk.FlowBoxChild create_color_button (string color) { + private Gtk.FlowBoxChild create_color_button (Lib.Components.Pattern pattern) { var child = new Gtk.FlowBoxChild () { valign = halign = Gtk.Align.CENTER }; - var btn = new RoundedColorButton (color); - btn.set_color.connect ((color) => { - var rgba_color = Gdk.RGBA (); - rgba_color.parse (color); - chooser.set_rgba (rgba_color); + var btn = new RoundedColorButton (pattern); + btn.set_pattern.connect ((pattern) => { + model.active_pattern_type = pattern.type; + model.pattern = pattern; + pattern_chooser.set_pattern_type (pattern.type); }); child.add (btn); diff --git a/src/Widgets/RoundedColorButton.vala b/src/Widgets/RoundedColorButton.vala index 63a87968..14443a55 100644 --- a/src/Widgets/RoundedColorButton.vala +++ b/src/Widgets/RoundedColorButton.vala @@ -21,9 +21,9 @@ */ public class Akira.Widgets.RoundedColorButton : Gtk.Grid { - public signal void set_color (string color); + public signal void set_pattern (Lib.Components.Pattern pattern); - public RoundedColorButton (string color) { + public RoundedColorButton (Lib.Components.Pattern pattern) { var context = get_style_context (); context.add_class ("saved-color-button"); context.add_class ("bg-pattern"); @@ -35,14 +35,15 @@ public class Akira.Widgets.RoundedColorButton : Gtk.Grid { btn.width_request = btn.height_request = 24; btn.valign = btn.halign = Gtk.Align.CENTER; btn.can_focus = false; - btn.tooltip_text = _("Set color to " + color); + // btn.tooltip_text = _("Set color to " + color); try { var provider = new Gtk.CssProvider (); + var css_pattern = Utils.Pattern.convert_to_css_linear_gradient (pattern); var css = """.color-item { - background-color: %s; - border-color: shade (%s, 0.75); - }""".printf (color, color); + background: %s; + border: none; + }""".printf (css_pattern); provider.load_from_data (css, css.length); btn_context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); @@ -52,7 +53,7 @@ public class Akira.Widgets.RoundedColorButton : Gtk.Grid { // Emit the set_color signal when the button is clicked. btn.clicked.connect (() => { - set_color (color); + set_pattern (pattern); }); add (btn); From c55876ae7169b12037f0a60d00811d71fa46d2f8 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Sat, 30 Apr 2022 22:32:23 +0530 Subject: [PATCH 19/21] Prvent weird color popover bug --- src/Widgets/ColorButton.vala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Widgets/ColorButton.vala b/src/Widgets/ColorButton.vala index 0aa61983..4734cceb 100644 --- a/src/Widgets/ColorButton.vala +++ b/src/Widgets/ColorButton.vala @@ -66,6 +66,13 @@ public class Akira.Widgets.ColorButton : Gtk.Button { color_popover.closed.connect (() => { window.event_bus.change_gradient_nobs_visibility (false); }); + + // This is for preventing a weird bug introduced by making the popover non-modal. + // When a item is selected, sometimes the popover would open and close real fast. + // Making it invisible after unrealizing prevents this bug. + color_popover.unrealize.connect (() => { + color_popover.visible = false; + }); } ~ColorButton () { From df0fae003e7341f723d34c0c4fd819ae5be6f96c Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Sun, 1 May 2022 13:21:12 +0530 Subject: [PATCH 20/21] Fix minor bug in gradient nobs --- src/Widgets/ColorButton.vala | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Widgets/ColorButton.vala b/src/Widgets/ColorButton.vala index 4734cceb..fca79e6f 100644 --- a/src/Widgets/ColorButton.vala +++ b/src/Widgets/ColorButton.vala @@ -140,6 +140,27 @@ public class Akira.Widgets.ColorButton : Gtk.Button { color_chooser.set_pattern (model.pattern); color_popover.popup (); + var canvas = window.main_window.main_view_canvas.canvas; + + var coords = canvas.selection_manager.selection.first_node ().instance.components.center; + var size = canvas.selection_manager.selection.first_node ().instance.components.size; + + Geometry.Point origin = Geometry.Point (coords.x - size.width / 2.0, coords.y - size.height / 2.0); + + // Update position of nobs in ViewLayerNobs. + var start_nob_pos = Geometry.Point ( + model.pattern.start.x * size.width / 100.0 + origin.x, + model.pattern.start.y * size.height / 100.0 + origin.y + ); + var end_nob_pos = Geometry.Point ( + model.pattern.end.x * size.width / 100.0 + origin.x, + model.pattern.end.y * size.height / 100.0 + origin.y + ); + + canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_START, start_nob_pos); + canvas.nob_manager.set_gradient_nob_position (Utils.Nobs.Nob.GRADIENT_END, end_nob_pos); + canvas.nob_manager.set_layer_flags_from_pattern_type (model.pattern.type); + window.event_bus.change_gradient_nobs_visibility (true); } } From 168a716cea433f1235d53bad36c4d50f16c86b78 Mon Sep 17 00:00:00 2001 From: Ashish Shevale Date: Sun, 1 May 2022 13:43:16 +0530 Subject: [PATCH 21/21] Fix some more silly mistakes --- src/Lib/Components/Borders.vala | 9 ++------- src/Utils/Nobs.vala | 8 ++++---- src/Utils/Pattern.vala | 36 ++++++++++++--------------------- src/Widgets/GradientEditor.vala | 5 +++++ 4 files changed, 24 insertions(+), 34 deletions(-) diff --git a/src/Lib/Components/Borders.vala b/src/Lib/Components/Borders.vala index 6c2d1dcd..345cd538 100644 --- a/src/Lib/Components/Borders.vala +++ b/src/Lib/Components/Borders.vala @@ -131,13 +131,8 @@ public class Akira.Lib.Components.Borders : Component, Copyable { } public Border with_replaced_pattern (Pattern new_pattern) { - var new_border = Border (); - - new_border._id = this._id; - new_border.active_pattern = new_pattern.type; - new_border.solid_pattern = this.solid_pattern; - new_border.linear_pattern = this.linear_pattern; - new_border.radial_pattern = this.radial_pattern; + var new_border = Border.with_all_patterns (_id, solid_pattern, linear_pattern, radial_pattern, new_pattern.type); + new_border._pattern = new_pattern; return new_border; } diff --git a/src/Utils/Nobs.vala b/src/Utils/Nobs.vala index 6b3fb9e7..004cc8fd 100644 --- a/src/Utils/Nobs.vala +++ b/src/Utils/Nobs.vala @@ -73,15 +73,15 @@ public class Akira.Utils.Nobs : Object { public NobData[] data; public NobSet () { - data = new NobData[13]; - for (var i = 0; i < 13; i++) { + data = new NobData[11]; + for (var i = 0; i < 11; i++) { data[i] = new NobData ((Nob)i, 0, 0, false); } } public NobSet.clone (NobSet other) { - data = new NobData[13]; - for (var i = 0; i < 13; i++) { + data = new NobData[11]; + for (var i = 0; i < 11; i++) { data[i] = other.data[i].copy (); } } diff --git a/src/Utils/Pattern.vala b/src/Utils/Pattern.vala index ab098a9c..f50fcc9e 100644 --- a/src/Utils/Pattern.vala +++ b/src/Utils/Pattern.vala @@ -27,20 +27,9 @@ public class Akira.Utils.Pattern { case Lib.Components.Pattern.PatternType.SOLID: var color = pattern.colors.first ().color; converted = new Cairo.Pattern.rgba (color.red, color.green, color.blue, color.alpha); - break; + return converted; case Lib.Components.Pattern.PatternType.LINEAR: converted = new Cairo.Pattern.linear (pattern.start.x, pattern.start.y, pattern.end.x, pattern.end.y); - - foreach (var stop_color in pattern.colors) { - var color = stop_color.color; - converted.add_color_stop_rgba ( - stop_color.offset, - color.red, - color.green, - color.blue, - color.alpha - ); - } break; case Lib.Components.Pattern.PatternType.RADIAL: double distance = Utils.GeometryMath.distance (pattern.start.x, pattern.start.y, pattern.end.x, pattern.end.y); @@ -52,17 +41,6 @@ public class Akira.Utils.Pattern { pattern.start.y, distance ); - - foreach (var stop_color in pattern.colors) { - var color = stop_color.color; - converted.add_color_stop_rgba ( - stop_color.offset, - color.red, - color.green, - color.blue, - color.alpha - ); - } break; default: assert (false); @@ -70,6 +48,18 @@ public class Akira.Utils.Pattern { break; } + // If the pattern was linear or radial, add all the stop colors. + foreach (var stop_color in pattern.colors) { + var color = stop_color.color; + converted.add_color_stop_rgba ( + stop_color.offset, + color.red, + color.green, + color.blue, + color.alpha + ); + } + return converted; } diff --git a/src/Widgets/GradientEditor.vala b/src/Widgets/GradientEditor.vala index 5665e731..091987f6 100644 --- a/src/Widgets/GradientEditor.vala +++ b/src/Widgets/GradientEditor.vala @@ -196,6 +196,11 @@ public class Akira.Widgets.GradientEditor : Gtk.DrawingArea { // As we used the BUTTON_MOTION_MASK, this method will be called only when mouse is clicked and dragged. private bool handle_motion_notify (Gdk.EventMotion event) { + if (selected_stop_color.offset == 0 || selected_stop_color.offset == 1) { + // First and last stop colors are not allowed to be moved. + return true; + } + double event_offset = event.x / width; pattern.colors.remove (selected_stop_color);