From 7120c733bd6caecdf766b4b65365c8a4a1eed5b6 Mon Sep 17 00:00:00 2001 From: Alessandro Date: Thu, 27 May 2021 21:40:54 -0700 Subject: [PATCH 01/14] Implement global colors (#599) * Create initial widgets and settings for global colors * Style color library * Improve styling and remove duplication --- data/css/layout.css | 10 ++ data/css/option-panel.css | 9 +- .../com.github.akiraux.akira.gschema.xml.in | 7 ++ src/Layouts/Partials/BorderItem.vala | 4 +- src/Layouts/Partials/FillItem.vala | 93 ++++++++++++++++++- src/Partials/AddColorButton.vala | 33 +++++++ src/Partials/RoundedColorButton.vala | 58 ++++++++++++ src/Services/Settings.vala | 6 ++ src/meson.build | 2 + 9 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 src/Partials/AddColorButton.vala create mode 100644 src/Partials/RoundedColorButton.vala diff --git a/data/css/layout.css b/data/css/layout.css index 1a2371a72..ef2461dfe 100644 --- a/data/css/layout.css +++ b/data/css/layout.css @@ -30,6 +30,16 @@ padding: 8px 5px; } +.color-grid flowboxchild { + background-color: transparent; +} + +.color-picker label { + font-size: 9pt; + font-weight: bold; + color: @fg_color; +} + .popover-toggler { padding: 3px; margin: 3px; diff --git a/data/css/option-panel.css b/data/css/option-panel.css index d2588ee67..0aca09aad 100644 --- a/data/css/option-panel.css +++ b/data/css/option-panel.css @@ -28,7 +28,10 @@ linear-gradient(to bottom, black 50%, white 50%); background-blend-mode: normal, difference, normal; background-size: 8px 8px; - border-radius: 3px 0 0 3px; +} + +.selected-color-container { + border-radius: 4px 0 0 4px; } button.selected-color { @@ -36,6 +39,10 @@ button.selected-color { border: 1px solid; } +.saved-color-button { + border-radius: 4px; +} + /* spin button */ .sidebar-l spinbutton.horizontal button.down, .sidebar-l spinbutton.horizontal button.up { diff --git a/data/schemas/com.github.akiraux.akira.gschema.xml.in b/data/schemas/com.github.akiraux.akira.gschema.xml.in index bfe2941d8..6f3fd7ecf 100644 --- a/data/schemas/com.github.akiraux.akira.gschema.xml.in +++ b/data/schemas/com.github.akiraux.akira.gschema.xml.in @@ -107,6 +107,7 @@ Default Border Width. The default width of the border for a newly created shape. + '#AAAAAA' Default Border Color. @@ -178,5 +179,11 @@ The alpha setting for exporting PNG images. The alpha setting for exporting PNG images in the export dialog. + + + [] + The list of global colors + Keep track of the colors saved by the user and available in every document file. + diff --git a/src/Layouts/Partials/BorderItem.vala b/src/Layouts/Partials/BorderItem.vala index 3600b4f03..55a65e918 100644 --- a/src/Layouts/Partials/BorderItem.vala +++ b/src/Layouts/Partials/BorderItem.vala @@ -109,7 +109,9 @@ public class Akira.Layouts.Partials.BorderItem : Gtk.Grid { color_chooser.margin_end = 5; var selected_color_container = new Gtk.Grid (); - selected_color_container.get_style_context ().add_class ("bg-pattern"); + var context = selected_color_container.get_style_context (); + context.add_class ("selected-color-container"); + context.add_class ("bg-pattern"); selected_color = new Gtk.Button (); selected_color.vexpand = true; diff --git a/src/Layouts/Partials/FillItem.vala b/src/Layouts/Partials/FillItem.vala index 9159b0244..5fe796cce 100644 --- a/src/Layouts/Partials/FillItem.vala +++ b/src/Layouts/Partials/FillItem.vala @@ -30,15 +30,26 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { private Gtk.Button delete_button; private Gtk.Image hidden_button_icon; private Gtk.Button selected_color; - public Akira.Partials.InputField opacity_container; - public Akira.Partials.ColorField color_container; + private Akira.Partials.InputField opacity_container; + private Akira.Partials.ColorField color_container; private Gtk.Popover color_popover; private Gtk.Grid color_picker; private Gtk.ColorChooserWidget? color_chooser_widget = null; private Akira.Utils.ColorPicker eyedropper; + private Gtk.FlowBox global_colors_flowbox; + public Akira.Models.FillsItemModel model { get; construct; } + /* + * 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. + */ + private enum Container { + GLOBAL, + DOCUMENT + } + private string old_color; // If the color or alpha are manually set from the ColorPicker. @@ -102,7 +113,9 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { fill_chooser.margin_end = 5; var selected_color_container = new Gtk.Grid (); - selected_color_container.get_style_context ().add_class ("bg-pattern"); + var context = selected_color_container.get_style_context (); + context.add_class ("selected-color-container"); + context.add_class ("bg-pattern"); selected_color = new Gtk.Button (); selected_color.vexpand = true; @@ -217,7 +230,36 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { color_picker = new Gtk.Grid (); color_picker.get_style_context ().add_class ("color-picker"); + color_picker.row_spacing = 12; + + var global_colors_label = new Gtk.Label (_("Global colors")); + global_colors_label.halign = Gtk.Align.START; + global_colors_label.margin_start = global_colors_label.margin_end = 6; + + global_colors_flowbox = new Gtk.FlowBox (); + global_colors_flowbox.get_style_context ().add_class ("color-grid"); + global_colors_flowbox.selection_mode = Gtk.SelectionMode.NONE; + global_colors_flowbox.homogeneous = false; + global_colors_flowbox.column_spacing = global_colors_flowbox.row_spacing = 6; + global_colors_flowbox.margin_start = global_colors_flowbox.margin_end = 6; + // Large number to allow children to spread out the available space. + global_colors_flowbox.max_children_per_line = 100; + global_colors_flowbox.set_sort_func (sort_colors_function); + + var add_global_color_btn = new Akira.Partials.AddColorButton (); + add_global_color_btn.clicked.connect (() => { + on_save_color (Container.GLOBAL); + }); + global_colors_flowbox.add (add_global_color_btn); + + foreach (string color in settings.global_colors) { + var btn = create_color_button (color); + global_colors_flowbox.add (btn); + } + color_picker.attach (color_chooser_widget, 0, 0, 1, 1); + color_picker.attach (global_colors_label, 0, 1, 1, 1); + color_picker.attach (global_colors_flowbox, 0, 2, 1, 1); color_picker.show_all (); color_popover.add (color_picker); @@ -231,6 +273,47 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { hidden_button.clicked.connect (toggle_visibility); } + /* + * Add the current color to the parent flowbox. + */ + private void on_save_color (Container parent) { + // Store the currently active color. + var color = color_chooser_widget.rgba.to_string (); + + // Create the new color button and connect to its signal. + var btn = create_color_button (color); + + // Update the colors list and the schema based on the colors container. + switch (parent) { + case Container.GLOBAL: + global_colors_flowbox.add (btn); + var array = settings.global_colors; + array += color; + settings.global_colors = array; + break; + + case Container.DOCUMENT: + // TODO... + break; + } + } + + private Gtk.FlowBoxChild create_color_button (string color) { + var child = new Gtk.FlowBoxChild (); + child.valign = child.halign = Gtk.Align.CENTER; + + var btn = new Akira.Partials.RoundedColorButton (color); + btn.set_color.connect ((color) => { + var rgba_color = Gdk.RGBA (); + rgba_color.parse (color); + color_chooser_widget.set_rgba (rgba_color); + }); + + child.add (btn); + child.show_all (); + return child; + } + private void on_eyedropper_click () { eyedropper = new Akira.Utils.ColorPicker (); eyedropper.show_all (); @@ -324,4 +407,8 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { color_chooser_widget.set_rgba (new_rgba); } + + private int sort_colors_function (Gtk.FlowBoxChild a, Gtk.FlowBoxChild b) { + return (a is Akira.Partials.AddColorButton) ? -1 : 1; + } } diff --git a/src/Partials/AddColorButton.vala b/src/Partials/AddColorButton.vala new file mode 100644 index 000000000..742967212 --- /dev/null +++ b/src/Partials/AddColorButton.vala @@ -0,0 +1,33 @@ +/* + * Copyright (c) 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: Abdallah "Abdallah-Moh" Mohammad + * Authored by: Alessandro "alecaddd" Castellani + */ + +/* + * Helper class to quickly create a simple style button with a + icon. + */ +public class Akira.Partials.AddColorButton : Gtk.Button { + public AddColorButton () { + get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); + width_request = height_request = 24; + can_focus = false; + valign = halign = Gtk.Align.CENTER; + image = new Gtk.Image.from_icon_name ("list-add-symbolic", Gtk.IconSize.MENU); + tooltip_text = _("Add the current color to the library"); + } +} diff --git a/src/Partials/RoundedColorButton.vala b/src/Partials/RoundedColorButton.vala new file mode 100644 index 000000000..2ba66a0d1 --- /dev/null +++ b/src/Partials/RoundedColorButton.vala @@ -0,0 +1,58 @@ +/* + * Copyright (c) 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: Abdallah "Abdallah-Moh" Mohammad + * Authored by: Alessandro "alecaddd" Castellani + */ + +public class Akira.Partials.RoundedColorButton : Gtk.Grid { + public signal void set_color (string color); + + public RoundedColorButton (string color) { + var context = get_style_context (); + context.add_class ("saved-color-button"); + context.add_class ("bg-pattern"); + valign = halign = Gtk.Align.CENTER; + + var btn = new Gtk.Button (); + var btn_context = btn.get_style_context (); + btn_context.add_class ("color-item"); + btn.width_request = btn.height_request = 24; + btn.valign = btn.halign = Gtk.Align.CENTER; + btn.can_focus = false; + btn.tooltip_text = color; + + try { + var provider = new Gtk.CssProvider (); + var css = """.color-item { + background-color: %s; + border-color: shade (%s, 0.75); + }""".printf (color, color); + + provider.load_from_data (css, css.length); + btn_context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + } catch (Error e) { + warning ("Style error: %s", e.message); + } + + // Emit the set_color signal when the button is clicked. + btn.clicked.connect (() => { + set_color (color); + }); + + add (btn); + } +} diff --git a/src/Services/Settings.vala b/src/Services/Settings.vala index f93fa0e06..2896247c7 100644 --- a/src/Services/Settings.vala +++ b/src/Services/Settings.vala @@ -149,6 +149,12 @@ public class Akira.Services.Settings : GLib.Settings { set { set_boolean ("export-alpha", value); } } + // Colors library. + public string[] global_colors { + owned get { return get_strv ("global-colors"); } + set { set_strv ("global-colors", value); } + } + public Settings () { Object (schema_id: Constants.APP_ID); } diff --git a/src/meson.build b/src/meson.build index f574e48a6..1a674a915 100644 --- a/src/meson.build +++ b/src/meson.build @@ -55,6 +55,7 @@ sources = files( 'Layouts/Partials/BorderRadiusPanel.vala', 'Layouts/Partials/AlignItemsPanel.vala', + 'Partials/AddColorButton.vala', 'Partials/HeaderBarButton.vala', 'Partials/MenuButton.vala', 'Partials/ZoomButton.vala', @@ -64,6 +65,7 @@ sources = files( 'Partials/ColorField.vala', 'Partials/LinkedInput.vala', 'Partials/PanelSeparator.vala', + 'Partials/RoundedColorButton.vala', 'Partials/ExportWidget.vala', 'Models/BordersItemModel.vala', From 3a5ee74d990ab73192386299637d7c0f8a23448f Mon Sep 17 00:00:00 2001 From: Alessandro Date: Fri, 4 Jun 2021 23:42:46 -0700 Subject: [PATCH 02/14] Partials widgets rename (#600) * Rename Partials to Widgets * Consistent block comment for all widgets * Disconnect events on destruction * Remove constructor and update window attribute to unowned * Update the ButtonImage widget --- src/Dialogs/ExportDialog.vala | 2 +- src/Layouts/HeaderBar.vala | 66 +++++++------- src/Layouts/Partials/AlignItemsPanel.vala | 2 +- src/Layouts/Partials/BorderItem.vala | 10 +-- src/Layouts/Partials/BorderRadiusPanel.vala | 30 +++---- src/Layouts/Partials/FillItem.vala | 18 ++-- src/Layouts/Partials/TransformPanel.vala | 36 ++++---- src/Partials/ButtonImage.vala | 44 --------- src/Partials/ColorField.vala | 87 ------------------ src/Partials/PanelSeparator.vala | 28 ------ src/{Partials => Widgets}/AddColorButton.vala | 4 +- src/{Partials => Widgets}/AlignBoxButton.vala | 31 +++---- src/Widgets/ButtonImage.vala | 47 ++++++++++ src/Widgets/ColorField.vala | 90 +++++++++++++++++++ src/{Partials => Widgets}/ExportWidget.vala | 8 +- .../HeaderBarButton.vala | 42 ++++----- src/{Partials => Widgets}/InputField.vala | 42 ++++----- src/{Partials => Widgets}/LinkedInput.vala | 54 +++++------ src/{Partials => Widgets}/MenuButton.vala | 45 +++++----- src/Widgets/PanelSeparator.vala | 28 ++++++ .../RoundedColorButton.vala | 4 +- src/{Partials => Widgets}/ZoomButton.vala | 6 +- src/meson.build | 24 ++--- 23 files changed, 379 insertions(+), 369 deletions(-) delete mode 100644 src/Partials/ButtonImage.vala delete mode 100644 src/Partials/ColorField.vala delete mode 100644 src/Partials/PanelSeparator.vala rename src/{Partials => Widgets}/AddColorButton.vala (95%) rename src/{Partials => Widgets}/AlignBoxButton.vala (56%) create mode 100644 src/Widgets/ButtonImage.vala create mode 100644 src/Widgets/ColorField.vala rename src/{Partials => Widgets}/ExportWidget.vala (97%) rename src/{Partials => Widgets}/HeaderBarButton.vala (61%) rename src/{Partials => Widgets}/InputField.vala (82%) rename src/{Partials => Widgets}/LinkedInput.vala (79%) rename src/{Partials => Widgets}/MenuButton.vala (53%) create mode 100644 src/Widgets/PanelSeparator.vala rename src/{Partials => Widgets}/RoundedColorButton.vala (97%) rename src/{Partials => Widgets}/ZoomButton.vala (99%) diff --git a/src/Dialogs/ExportDialog.vala b/src/Dialogs/ExportDialog.vala index 5cba61ccf..e8c755767 100644 --- a/src/Dialogs/ExportDialog.vala +++ b/src/Dialogs/ExportDialog.vala @@ -111,7 +111,7 @@ public class Akira.Dialogs.ExportDialog : Gtk.Dialog { export_grid.get_style_context ().add_class ("export-panel"); export_grid.bind_model (list_store, model => { - return new Akira.Partials.ExportWidget (model as Akira.Models.ExportModel); + return new Widgets.ExportWidget (model as Akira.Models.ExportModel); }); var scrolled = new Gtk.ScrolledWindow (null, null); diff --git a/src/Layouts/HeaderBar.vala b/src/Layouts/HeaderBar.vala index 99910c82c..9d4c94d99 100644 --- a/src/Layouts/HeaderBar.vala +++ b/src/Layouts/HeaderBar.vala @@ -24,25 +24,25 @@ public class Akira.Layouts.HeaderBar : Gtk.HeaderBar { private Lib.Items.CanvasItem selected_item; - public Akira.Partials.HeaderBarButton new_document; - public Akira.Partials.HeaderBarButton save_file; - public Akira.Partials.HeaderBarButton save_file_as; + public Widgets.HeaderBarButton new_document; + public Widgets.HeaderBarButton save_file; + public Widgets.HeaderBarButton save_file_as; public Gtk.Grid recent_files_grid; - public Akira.Partials.MenuButton menu; - public Akira.Partials.MenuButton items; - public Akira.Partials.ZoomButton zoom; - public Akira.Partials.HeaderBarButton group; - public Akira.Partials.HeaderBarButton ungroup; - public Akira.Partials.HeaderBarButton move_up; - public Akira.Partials.HeaderBarButton move_down; - public Akira.Partials.HeaderBarButton move_top; - public Akira.Partials.HeaderBarButton move_bottom; - public Akira.Partials.HeaderBarButton preferences; - public Akira.Partials.HeaderBarButton path_difference; - public Akira.Partials.HeaderBarButton path_exclusion; - public Akira.Partials.HeaderBarButton path_intersect; - public Akira.Partials.HeaderBarButton path_union; + public Widgets.MenuButton menu; + public Widgets.MenuButton items; + public Widgets.ZoomButton zoom; + public Widgets.HeaderBarButton group; + public Widgets.HeaderBarButton ungroup; + public Widgets.HeaderBarButton move_up; + public Widgets.HeaderBarButton move_down; + public Widgets.HeaderBarButton move_top; + public Widgets.HeaderBarButton move_bottom; + public Widgets.HeaderBarButton preferences; + public Widgets.HeaderBarButton path_difference; + public Widgets.HeaderBarButton path_exclusion; + public Widgets.HeaderBarButton path_intersect; + public Widgets.HeaderBarButton path_union; public Gtk.PopoverMenu popover_insert; @@ -66,64 +66,64 @@ public class Akira.Layouts.HeaderBar : Gtk.HeaderBar { set_show_close_button (true); title = _("Untitled"); - menu = new Akira.Partials.MenuButton ("document-open", _("Menu"), null); + menu = new Widgets.MenuButton ("document-open", _("Menu"), null); var menu_popover = build_main_menu_popover (); menu.button.popover = menu_popover; - items = new Akira.Partials.MenuButton ("insert-object", _("Insert"), null); + items = new Widgets.MenuButton ("insert-object", _("Insert"), null); var items_popover = build_items_popover (); items.button.popover = items_popover; - zoom = new Akira.Partials.ZoomButton (window); + zoom = new Widgets.ZoomButton (window); - group =new Akira.Partials.HeaderBarButton (window, "object-group", + group =new Widgets.HeaderBarButton (window, "object-group", _("Group"), {"g"}, "multiple"); - ungroup = new Akira.Partials.HeaderBarButton (window, "object-ungroup", + ungroup = new Widgets.HeaderBarButton (window, "object-ungroup", _("Ungroup"), {"g"}, "group"); - move_up = new Akira.Partials.HeaderBarButton (window, "selection-raise", + move_up = new Widgets.HeaderBarButton (window, "selection-raise", _("Up"), {"Up"}, "single"); move_up.button.action_name = Akira.Services.ActionManager.ACTION_PREFIX + Akira.Services.ActionManager.ACTION_MOVE_UP; - move_down = new Akira.Partials.HeaderBarButton (window, "selection-lower", + move_down = new Widgets.HeaderBarButton (window, "selection-lower", _("Down"), {"Down"}, "single"); move_down.button.action_name = Akira.Services.ActionManager.ACTION_PREFIX + Akira.Services.ActionManager.ACTION_MOVE_DOWN; - move_top = new Akira.Partials.HeaderBarButton (window, "selection-top", + move_top = new Widgets.HeaderBarButton (window, "selection-top", _("Top"), {"Up"}, "single"); move_top.button.action_name = Akira.Services.ActionManager.ACTION_PREFIX + Akira.Services.ActionManager.ACTION_MOVE_TOP; - move_bottom = new Akira.Partials.HeaderBarButton (window, "selection-bottom", + move_bottom = new Widgets.HeaderBarButton (window, "selection-bottom", _("Bottom"), {"Down"}, "single"); move_bottom.button.action_name = Akira.Services.ActionManager.ACTION_PREFIX + Akira.Services.ActionManager.ACTION_MOVE_BOTTOM; - preferences = new Akira.Partials.HeaderBarButton (window, "open-menu", + preferences = new Widgets.HeaderBarButton (window, "open-menu", _("Settings"), {"comma"}); preferences.button.action_name = Akira.Services.ActionManager.ACTION_PREFIX + Akira.Services.ActionManager.ACTION_PREFERENCES; preferences.sensitive = true; - var export = new Akira.Partials.MenuButton ("document-export", _("Export"), null); + var export = new Widgets.MenuButton ("document-export", _("Export"), null); var export_popover = build_export_popover (); export.button.popover = export_popover; export.sensitive = true; - var layout = new Akira.Partials.MenuButton ("document-layout", _("Layout"), null); + var layout = new Widgets.MenuButton ("document-layout", _("Layout"), null); var layout_popover = build_layout_popover (); layout.button.popover = layout_popover; layout.sensitive = true; - path_difference = new Akira.Partials.HeaderBarButton (window, "path-difference", + path_difference = new Widgets.HeaderBarButton (window, "path-difference", _("Difference"), null, "multiple"); - path_exclusion = new Akira.Partials.HeaderBarButton (window, "path-exclusion", + path_exclusion = new Widgets.HeaderBarButton (window, "path-exclusion", _("Exclusion"), null, "multiple"); - path_intersect = new Akira.Partials.HeaderBarButton (window, "path-intersection", + path_intersect = new Widgets.HeaderBarButton (window, "path-intersection", _("Intersect"), null, "multiple"); - path_union = new Akira.Partials.HeaderBarButton (window, "path-union", + path_union = new Widgets.HeaderBarButton (window, "path-union", _("Union"), null, "multiple"); pack_start (menu); diff --git a/src/Layouts/Partials/AlignItemsPanel.vala b/src/Layouts/Partials/AlignItemsPanel.vala index 554973d89..f7915c08d 100644 --- a/src/Layouts/Partials/AlignItemsPanel.vala +++ b/src/Layouts/Partials/AlignItemsPanel.vala @@ -82,7 +82,7 @@ public class Akira.Layouts.Partials.AlignItemsPanel : Gtk.Grid { case "btn": var tmp_align_box_button = - new Akira.Partials.AlignBoxButton ( + new Widgets.AlignBoxButton ( window, item.action_name, item.icon_name, diff --git a/src/Layouts/Partials/BorderItem.vala b/src/Layouts/Partials/BorderItem.vala index 55a65e918..448718b68 100644 --- a/src/Layouts/Partials/BorderItem.vala +++ b/src/Layouts/Partials/BorderItem.vala @@ -29,8 +29,8 @@ public class Akira.Layouts.Partials.BorderItem : Gtk.Grid { private Gtk.Button delete_button; private Gtk.Image hidden_button_icon; private Gtk.Button selected_color; - public Akira.Partials.InputField tickness_container; - public Akira.Partials.ColorField color_container; + public Widgets.InputField tickness_container; + public Widgets.ColorField color_container; private Gtk.Popover color_popover; private Gtk.Grid color_picker; private Akira.Utils.ColorPicker eyedropper; @@ -145,7 +145,7 @@ public class Akira.Layouts.Partials.BorderItem : Gtk.Grid { picker_container.add (selected_color_container); picker_container.add (eyedropper_button); - color_container = new Akira.Partials.ColorField (window); + color_container = new Widgets.ColorField (window); color_container.text = Utils.Color.rgba_to_hex (color); model.bind_property ( @@ -173,8 +173,8 @@ public class Akira.Layouts.Partials.BorderItem : Gtk.Grid { } ); - tickness_container = new Akira.Partials.InputField ( - Akira.Partials.InputField.Unit.PIXEL, 7, true, true); + tickness_container = new Widgets.InputField ( + Widgets.InputField.Unit.PIXEL, 7, true, true); tickness_container.set_range (0, Akira.Layouts.MainCanvas.CANVAS_SIZE / 2); tickness_container.entry.sensitive = true; tickness_container.entry.value = border_size; diff --git a/src/Layouts/Partials/BorderRadiusPanel.vala b/src/Layouts/Partials/BorderRadiusPanel.vala index a6a686084..6e55bc76a 100644 --- a/src/Layouts/Partials/BorderRadiusPanel.vala +++ b/src/Layouts/Partials/BorderRadiusPanel.vala @@ -28,13 +28,13 @@ public class Akira.Layouts.Partials.BorderRadiusPanel : Gtk.Grid { private Gtk.Grid options_grid; private Gtk.Adjustment radius_adj; private Gtk.Scale border_radius_scale; - private Akira.Partials.InputField border_radius_entry; + private Widgets.InputField border_radius_entry; private Gtk.ToggleButton options_button; - private Akira.Partials.InputField border_radius_bottom_left_entry; - private Akira.Partials.InputField border_radius_bottom_right_entry; - private Akira.Partials.InputField border_radius_top_left_entry; - private Akira.Partials.InputField border_radius_top_right_entry; + private Widgets.InputField border_radius_bottom_left_entry; + private Widgets.InputField border_radius_bottom_right_entry; + private Widgets.InputField border_radius_top_left_entry; + private Widgets.InputField border_radius_top_right_entry; private Gtk.Switch autoscale_switch; private Gtk.Switch uniform_switch; @@ -116,8 +116,8 @@ public class Akira.Layouts.Partials.BorderRadiusPanel : Gtk.Grid { border_radius_scale.hexpand = true; panel_grid.attach (border_radius_scale, 0, 2, 1, 1); - border_radius_entry = new Akira.Partials.InputField ( - Akira.Partials.InputField.Unit.PIXEL, 7, true, true); + border_radius_entry = new Widgets.InputField ( + Widgets.InputField.Unit.PIXEL, 7, true, true); border_radius_entry.entry.hexpand = false; border_radius_entry.entry.width_request = 64; border_radius_entry.valign = Gtk.Align.CENTER; @@ -151,32 +151,32 @@ public class Akira.Layouts.Partials.BorderRadiusPanel : Gtk.Grid { border_entries_grid.column_spacing = 12; border_entries_grid.hexpand = true; - border_radius_top_left_entry = new Akira.Partials.InputField ( - Akira.Partials.InputField.Unit.PIXEL, 7, true, true); + border_radius_top_left_entry = new Widgets.InputField ( + Widgets.InputField.Unit.PIXEL, 7, true, true); border_radius_top_left_entry.entry.hexpand = false; border_radius_top_left_entry.entry.width_request = 64; border_radius_top_left_entry.valign = Gtk.Align.CENTER; border_radius_top_left_entry.halign = Gtk.Align.START; border_entries_grid.attach (border_radius_top_left_entry, 0, 0, 1, 1); - border_radius_top_right_entry = new Akira.Partials.InputField ( - Akira.Partials.InputField.Unit.PIXEL, 7, true, true); + border_radius_top_right_entry = new Widgets.InputField ( + Widgets.InputField.Unit.PIXEL, 7, true, true); border_radius_top_right_entry.entry.hexpand = false; border_radius_top_right_entry.entry.width_request = 64; border_radius_top_right_entry.valign = Gtk.Align.CENTER; border_radius_top_right_entry.halign = Gtk.Align.END; border_entries_grid.attach (border_radius_top_right_entry, 1, 0, 1, 1); - border_radius_bottom_left_entry = new Akira.Partials.InputField ( - Akira.Partials.InputField.Unit.PIXEL, 7, true, true); + border_radius_bottom_left_entry = new Widgets.InputField ( + Widgets.InputField.Unit.PIXEL, 7, true, true); border_radius_bottom_left_entry.entry.hexpand = false; border_radius_bottom_left_entry.entry.width_request = 64; border_radius_bottom_left_entry.valign = Gtk.Align.CENTER; border_radius_bottom_left_entry.halign = Gtk.Align.START; border_entries_grid.attach (border_radius_bottom_left_entry, 0, 1, 1, 1); - border_radius_bottom_right_entry = new Akira.Partials.InputField ( - Akira.Partials.InputField.Unit.PIXEL, 7, true, true); + border_radius_bottom_right_entry = new Widgets.InputField ( + Widgets.InputField.Unit.PIXEL, 7, true, true); border_radius_bottom_right_entry.entry.hexpand = false; border_radius_bottom_right_entry.entry.width_request = 64; border_radius_bottom_right_entry.valign = Gtk.Align.CENTER; diff --git a/src/Layouts/Partials/FillItem.vala b/src/Layouts/Partials/FillItem.vala index 5fe796cce..a18d7799c 100644 --- a/src/Layouts/Partials/FillItem.vala +++ b/src/Layouts/Partials/FillItem.vala @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2019-2021 Alecaddd (https://alecaddd.com) * * This file is part of Akira. @@ -30,8 +30,8 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { private Gtk.Button delete_button; private Gtk.Image hidden_button_icon; private Gtk.Button selected_color; - private Akira.Partials.InputField opacity_container; - private Akira.Partials.ColorField color_container; + private Widgets.InputField opacity_container; + private Widgets.ColorField color_container; private Gtk.Popover color_popover; private Gtk.Grid color_picker; private Gtk.ColorChooserWidget? color_chooser_widget = null; @@ -149,7 +149,7 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { picker_container.add (selected_color_container); picker_container.add (eyedropper_button); - color_container = new Akira.Partials.ColorField (window); + color_container = new Widgets.ColorField (window); color_container.text = Utils.Color.rgba_to_hex (color); model.bind_property ( @@ -177,8 +177,8 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { } ); - opacity_container = new Akira.Partials.InputField ( - Akira.Partials.InputField.Unit.PERCENTAGE, 7, true, true); + opacity_container = new Widgets.InputField ( + Widgets.InputField.Unit.PERCENTAGE, 7, true, true); opacity_container.entry.sensitive = true; opacity_container.entry.value = Math.round ((double) alpha / 255 * 100); @@ -246,7 +246,7 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { global_colors_flowbox.max_children_per_line = 100; global_colors_flowbox.set_sort_func (sort_colors_function); - var add_global_color_btn = new Akira.Partials.AddColorButton (); + var add_global_color_btn = new Widgets.AddColorButton (); add_global_color_btn.clicked.connect (() => { on_save_color (Container.GLOBAL); }); @@ -302,7 +302,7 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { var child = new Gtk.FlowBoxChild (); child.valign = child.halign = Gtk.Align.CENTER; - var btn = new Akira.Partials.RoundedColorButton (color); + var btn = new Widgets.RoundedColorButton (color); btn.set_color.connect ((color) => { var rgba_color = Gdk.RGBA (); rgba_color.parse (color); @@ -409,6 +409,6 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { } private int sort_colors_function (Gtk.FlowBoxChild a, Gtk.FlowBoxChild b) { - return (a is Akira.Partials.AddColorButton) ? -1 : 1; + return (a is Widgets.AddColorButton) ? -1 : 1; } } diff --git a/src/Layouts/Partials/TransformPanel.vala b/src/Layouts/Partials/TransformPanel.vala index 6c522fec2..9a33b4e1b 100644 --- a/src/Layouts/Partials/TransformPanel.vala +++ b/src/Layouts/Partials/TransformPanel.vala @@ -24,16 +24,16 @@ public class Akira.Layouts.Partials.TransformPanel : Gtk.Grid { public weak Akira.Window window { get; construct; } public weak Akira.Lib.Canvas canvas; - private Akira.Partials.LinkedInput x; - private Akira.Partials.LinkedInput y; - private Akira.Partials.LinkedInput width; - private Akira.Partials.LinkedInput height; - private Akira.Partials.LinkedInput rotation; + private Widgets.LinkedInput x; + private Widgets.LinkedInput y; + private Widgets.LinkedInput width; + private Widgets.LinkedInput height; + private Widgets.LinkedInput rotation; private Gtk.ToggleButton lock_changes; private Gtk.ToggleButton hflip_button; private Gtk.ToggleButton vflip_button; private Gtk.Adjustment opacity_adj; - private Akira.Partials.InputField opacity_entry; + private Widgets.InputField opacity_entry; private Gtk.Scale scale; // Bindings. @@ -94,13 +94,13 @@ public class Akira.Layouts.Partials.TransformPanel : Gtk.Grid { column_spacing = 6; hexpand = true; - x = new Akira.Partials.LinkedInput (_("X"), _("Horizontal position")); + x = new Widgets.LinkedInput (_("X"), _("Horizontal position")); x.input_field.set_range (-Akira.Layouts.MainCanvas.CANVAS_SIZE, Akira.Layouts.MainCanvas.CANVAS_SIZE); - y = new Akira.Partials.LinkedInput (_("Y"), _("Vertical position")); + y = new Widgets.LinkedInput (_("Y"), _("Vertical position")); y.input_field.set_range (-Akira.Layouts.MainCanvas.CANVAS_SIZE, Akira.Layouts.MainCanvas.CANVAS_SIZE); - width = new Akira.Partials.LinkedInput (_("W"), _("Width")); + width = new Widgets.LinkedInput (_("W"), _("Width")); width.input_field.set_range (0, Akira.Layouts.MainCanvas.CANVAS_SIZE); - height = new Akira.Partials.LinkedInput (_("H"), _("Height")); + height = new Widgets.LinkedInput (_("H"), _("Height")); height.input_field.set_range (0, Akira.Layouts.MainCanvas.CANVAS_SIZE); var lock_image = new Gtk.Image.from_icon_name ("changes-allow-symbolic", Gtk.IconSize.BUTTON); @@ -116,11 +116,11 @@ public class Akira.Layouts.Partials.TransformPanel : Gtk.Grid { lock_changes.image = new Gtk.Image.from_icon_name (icon, Gtk.IconSize.BUTTON); }); - rotation = new Akira.Partials.LinkedInput (_("R"), _("Rotation degrees"), "°"); + rotation = new Widgets.LinkedInput (_("R"), _("Rotation degrees"), "°"); rotation.input_field.set_range (-360, 360); hflip_button = new Gtk.ToggleButton (); - hflip_button.add (new Akira.Partials.ButtonImage ("object-flip-horizontal")); + hflip_button.add (new Widgets.ButtonImage ("object-flip-horizontal")); hflip_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); hflip_button.hexpand = false; hflip_button.can_focus = false; @@ -131,7 +131,7 @@ public class Akira.Layouts.Partials.TransformPanel : Gtk.Grid { Granite.markup_accel_tooltip ({"bracketleft"}, _("Flip Horizontally")); vflip_button = new Gtk.ToggleButton (); - vflip_button.add (new Akira.Partials.ButtonImage ("object-flip-vertical")); + vflip_button.add (new Widgets.ButtonImage ("object-flip-vertical")); vflip_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); vflip_button.hexpand = false; vflip_button.can_focus = false; @@ -154,8 +154,8 @@ public class Akira.Layouts.Partials.TransformPanel : Gtk.Grid { scale.draw_value = false; scale.digits = 0; scale.margin_end = 20; - opacity_entry = new Akira.Partials.InputField ( - Akira.Partials.InputField.Unit.PERCENTAGE, 7, true, true); + opacity_entry = new Widgets.InputField ( + Widgets.InputField.Unit.PERCENTAGE, 7, true, true); opacity_entry.entry.bind_property ( "value", opacity_adj, "value", BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE); @@ -170,16 +170,16 @@ public class Akira.Layouts.Partials.TransformPanel : Gtk.Grid { attach (group_title (_("Position")), 0, 0, 3); attach (x, 0, 1, 1); attach (y, 2, 1, 1); - attach (new Akira.Partials.PanelSeparator (), 0, 2, 3); + attach (new Widgets.PanelSeparator (), 0, 2, 3); attach (group_title (_("Size")), 0, 3, 3); attach (width, 0, 4, 1); attach (lock_changes, 1, 4, 1); attach (height, 2, 4, 1); - attach (new Akira.Partials.PanelSeparator (), 0, 5, 3); + attach (new Widgets.PanelSeparator (), 0, 5, 3); attach (group_title (_("Transform")), 0, 6, 3); attach (rotation, 0, 7, 1); attach (align_grid, 2, 7, 1); - attach (new Akira.Partials.PanelSeparator (), 0, 8, 3); + attach (new Widgets.PanelSeparator (), 0, 8, 3); attach (group_title (_("Opacity")), 0, 9, 3); attach (opacity_grid, 0, 10, 3); diff --git a/src/Partials/ButtonImage.vala b/src/Partials/ButtonImage.vala deleted file mode 100644 index e2352225b..000000000 --- a/src/Partials/ButtonImage.vala +++ /dev/null @@ -1,44 +0,0 @@ -/* -* Copyright (c) 2019 Alecaddd (http://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: Bilal Elmoussaoui -*/ - -public class Akira.Partials.ButtonImage: Gtk.Image { - - private string icon; - private Gtk.IconSize size; - - public ButtonImage (string icon_name, Gtk.IconSize icon_size = Gtk.IconSize.LARGE_TOOLBAR) { - icon = icon_name; - size = icon_size; - margin = 0; - - settings.changed["use-symbolic"].connect ( () => { - update_image (); - }); - - update_image (); - } - - private void update_image () { - var size = settings.use_symbolic ? Gtk.IconSize.SMALL_TOOLBAR : size; - var icon = settings.use_symbolic ? ("%s-symbolic".printf (icon)) : icon.replace ("-symbolic", ""); - set_from_icon_name (icon, size); - } -} diff --git a/src/Partials/ColorField.vala b/src/Partials/ColorField.vala deleted file mode 100644 index 5e9050b0f..000000000 --- a/src/Partials/ColorField.vala +++ /dev/null @@ -1,87 +0,0 @@ -/* -* Copyright (c) 2019-2020 Alecaddd (https://alecaddd.com) -* -* This file is part of Akira. -* -* Akira is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. - -* Akira is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. - -* You should have received a copy of the GNU General Public License -* along with Akira. If not, see . -* -* Authored by: Alessandro "Alecaddd" Castellani -*/ - -public class Akira.Partials.ColorField : Gtk.Entry { - public weak Akira.Window window { get; construct; } - - public ColorField (Akira.Window window) { - Object ( - window: window - ); - } - - construct { - margin_end = 10; - width_chars = 8; - max_length = 7; - hexpand = true; - - focus_in_event.connect (handle_focus_in); - focus_out_event.connect (handle_focus_out); - - insert_text.connect ((_new_text, new_text_length, ref position) => { - string new_text = _new_text.strip (); - - if (new_text.contains ("#")) { - new_text = new_text.substring (1, new_text.length - 1); - } else if (!this.text.contains ("#")) { - GLib.Signal.stop_emission_by_name (this, "insert-text"); - - var builder = new StringBuilder (); - builder.append (new_text); - builder.prepend ("#"); - this.text = builder.str; - - position = this.text.length; - } - - bool is_valid_hex = true; - bool char_is_numeric = true; - bool char_is_valid_alpha = true; - - char keyval; - - for (var i = 0; i < new_text.length; i++) { - keyval = new_text [i]; - - char_is_numeric = keyval >= Gdk.Key.@0 && keyval <= Gdk.Key.@9; - char_is_valid_alpha = keyval >= Gdk.Key.A && keyval <= Gdk.Key.F; - - is_valid_hex &= keyval.isxdigit (); - } - - if (!is_valid_hex) { - GLib.Signal.stop_emission_by_name (this, "insert-text"); - return; - } - }); - } - - private bool handle_focus_in (Gdk.EventFocus event) { - window.event_bus.disconnect_typing_accel (); - return false; - } - - private bool handle_focus_out (Gdk.EventFocus event) { - window.event_bus.connect_typing_accel (); - return false; - } -} diff --git a/src/Partials/PanelSeparator.vala b/src/Partials/PanelSeparator.vala deleted file mode 100644 index 6b4cf02db..000000000 --- a/src/Partials/PanelSeparator.vala +++ /dev/null @@ -1,28 +0,0 @@ -/* -* Copyright (c) 2019 Alecaddd (http://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: Alessandro "Alecaddd" Castellani -*/ - -public class Akira.Partials.PanelSeparator : Gtk.Separator { - construct { - orientation = Gtk.Orientation.HORIZONTAL; - get_style_context ().add_class ("panel-separator"); - margin_bottom = 6; - } -} diff --git a/src/Partials/AddColorButton.vala b/src/Widgets/AddColorButton.vala similarity index 95% rename from src/Partials/AddColorButton.vala rename to src/Widgets/AddColorButton.vala index 742967212..7f53c42f5 100644 --- a/src/Partials/AddColorButton.vala +++ b/src/Widgets/AddColorButton.vala @@ -7,10 +7,12 @@ * 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 . * @@ -21,7 +23,7 @@ /* * Helper class to quickly create a simple style button with a + icon. */ -public class Akira.Partials.AddColorButton : Gtk.Button { +public class Akira.Widgets.AddColorButton : Gtk.Button { public AddColorButton () { get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); width_request = height_request = 24; diff --git a/src/Partials/AlignBoxButton.vala b/src/Widgets/AlignBoxButton.vala similarity index 56% rename from src/Partials/AlignBoxButton.vala rename to src/Widgets/AlignBoxButton.vala index 3f54581ee..bc31776e6 100644 --- a/src/Partials/AlignBoxButton.vala +++ b/src/Widgets/AlignBoxButton.vala @@ -1,31 +1,32 @@ /* - * Copyright (c) 2019 Alecaddd (http://alecaddd.com) + * Copyright (c) 2019-2021 Alecaddd (https://alecaddd.com) * - * This program 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 2 of the License, or (at your option) any later version. + * This file is part of Akira. * - * This program is distributed in the hope that it will be useful, + * 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. + * 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 this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA + * You should have received a copy of the GNU General Public License + * along with Akira. If not, see . * * Authored by: Giacomo "giacomoalbe" Alberini */ -public class Akira.Partials.AlignBoxButton : Gtk.Button { - public signal void triggered (Akira.Partials.AlignBoxButton emitter); + +public class Akira.Widgets.AlignBoxButton : Gtk.Button { + public signal void triggered (AlignBoxButton emitter); public weak Akira.Window window { get; construct; } public string icon { get; construct; } public string action { get; construct; } - public Akira.Partials.ButtonImage btn_image; + public ButtonImage btn_image; public AlignBoxButton (Akira.Window window, string action_name, string icon_name, string tooltip, string[] accels) { Object ( diff --git a/src/Widgets/ButtonImage.vala b/src/Widgets/ButtonImage.vala new file mode 100644 index 000000000..01094322e --- /dev/null +++ b/src/Widgets/ButtonImage.vala @@ -0,0 +1,47 @@ +/* + * 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: Bilal Elmoussaoui + */ + +public class Akira.Widgets.ButtonImage: Gtk.Image { + private string icon; + private Gtk.IconSize size; + + public ButtonImage (string icon_name, Gtk.IconSize icon_size = Gtk.IconSize.LARGE_TOOLBAR) { + icon = icon_name; + size = icon_size; + margin = 0; + + settings.changed["use-symbolic"].connect (update_image); + update_image (); + } + + ~ButtonImage () { + settings.changed["use-symbolic"].disconnect (update_image); + } + + private void update_image () { + if (settings.use_symbolic) { + set_from_icon_name ("%s-symbolic".printf (icon), Gtk.IconSize.SMALL_TOOLBAR); + return; + } + + set_from_icon_name (icon.replace ("-symbolic", ""), size); + } +} diff --git a/src/Widgets/ColorField.vala b/src/Widgets/ColorField.vala new file mode 100644 index 000000000..3d16399f8 --- /dev/null +++ b/src/Widgets/ColorField.vala @@ -0,0 +1,90 @@ +/* + * 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: Alessandro "Alecaddd" Castellani + */ + +public class Akira.Widgets.ColorField : Gtk.Entry { + private unowned Akira.Window window; + + public ColorField (Akira.Window window) { + this.window = window; + + margin_end = 10; + width_chars = 8; + max_length = 7; + hexpand = true; + + focus_in_event.connect (handle_focus_in); + focus_out_event.connect (handle_focus_out); + insert_text.connect (handle_insert_text); + } + + ~ColorField () { + focus_in_event.disconnect (handle_focus_in); + focus_out_event.disconnect (handle_focus_out); + insert_text.disconnect (handle_insert_text); + } + + private void handle_insert_text (string text, int length, ref int position) { + string new_text = text.strip (); + + if (new_text.contains ("#")) { + new_text = new_text.substring (1, new_text.length - 1); + } else if (!this.text.contains ("#")) { + GLib.Signal.stop_emission_by_name (this, "insert-text"); + + var builder = new StringBuilder (); + builder.append (new_text); + builder.prepend ("#"); + this.text = builder.str; + + position = this.text.length; + } + + bool is_valid_hex = true; + bool char_is_numeric = true; + bool char_is_valid_alpha = true; + + char keyval; + + for (var i = 0; i < new_text.length; i++) { + keyval = new_text [i]; + + char_is_numeric = keyval >= Gdk.Key.@0 && keyval <= Gdk.Key.@9; + char_is_valid_alpha = keyval >= Gdk.Key.A && keyval <= Gdk.Key.F; + + is_valid_hex &= keyval.isxdigit (); + } + + if (!is_valid_hex) { + GLib.Signal.stop_emission_by_name (this, "insert-text"); + return; + } + } + + private bool handle_focus_in (Gdk.EventFocus event) { + window.event_bus.disconnect_typing_accel (); + return false; + } + + private bool handle_focus_out (Gdk.EventFocus event) { + window.event_bus.connect_typing_accel (); + return false; + } +} diff --git a/src/Partials/ExportWidget.vala b/src/Widgets/ExportWidget.vala similarity index 97% rename from src/Partials/ExportWidget.vala rename to src/Widgets/ExportWidget.vala index 0571eb678..186c0cd31 100644 --- a/src/Partials/ExportWidget.vala +++ b/src/Widgets/ExportWidget.vala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Alecaddd (https://alecaddd.com) + * Copyright (c) 2020-2021 Alecaddd (https://alecaddd.com) * * This file is part of Akira. * @@ -7,19 +7,19 @@ * 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: Alessandro "alecaddd" Castellani */ -public class Akira.Partials.ExportWidget : Gtk.Grid { +public class Akira.Widgets.ExportWidget : Gtk.Grid { private const int MB = 1024 * 1024; private const int KB = 1024; diff --git a/src/Partials/HeaderBarButton.vala b/src/Widgets/HeaderBarButton.vala similarity index 61% rename from src/Partials/HeaderBarButton.vala rename to src/Widgets/HeaderBarButton.vala index 70c39fa5e..2cb668adc 100644 --- a/src/Partials/HeaderBarButton.vala +++ b/src/Widgets/HeaderBarButton.vala @@ -1,29 +1,29 @@ /* -* Copyright (c) 2019 Alecaddd (http://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. + * 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: Alessandro "Alecaddd" Castellani + */ -* 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: Alessandro "Alecaddd" Castellani -*/ - -public class Akira.Partials.HeaderBarButton : Gtk.Grid { +public class Akira.Widgets.HeaderBarButton : Gtk.Grid { public weak Akira.Window window; public Gtk.Button button; private Gtk.Label label_btn; - public Akira.Partials.ButtonImage image; + public ButtonImage image; public string? sensitive_type; public HeaderBarButton (Akira.Window _window, string icon_name, string name, diff --git a/src/Partials/InputField.vala b/src/Widgets/InputField.vala similarity index 82% rename from src/Partials/InputField.vala rename to src/Widgets/InputField.vala index 2ca997c2b..b7aef6bd2 100644 --- a/src/Partials/InputField.vala +++ b/src/Widgets/InputField.vala @@ -1,25 +1,25 @@ /* -* Copyright (c) 2019-2020 Alecaddd (https://alecaddd.com) -* -* This file is part of Akira. -* -* Akira is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. - -* Akira is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. - -* You should have received a copy of the GNU General Public License -* along with Akira. If not, see . -* -* Authored by: Alessandro "Alecaddd" Castellani -*/ - -public class Akira.Partials.InputField : Gtk.EventBox { + * 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: Alessandro "Alecaddd" Castellani + */ + +public class Akira.Widgets.InputField : Gtk.EventBox { public Gtk.SpinButton entry { get; construct set; } public int chars { get; construct set; } diff --git a/src/Partials/LinkedInput.vala b/src/Widgets/LinkedInput.vala similarity index 79% rename from src/Partials/LinkedInput.vala rename to src/Widgets/LinkedInput.vala index c7a3338e8..12d156b4e 100644 --- a/src/Partials/LinkedInput.vala +++ b/src/Widgets/LinkedInput.vala @@ -1,32 +1,32 @@ /* -* Copyright (c) 2019-2020 Alecaddd (https://alecaddd.com) -* -* This file is part of Akira. -* -* Akira is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. - -* Akira is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. - -* You should have received a copy of the GNU General Public License -* along with Akira. If not, see . -* -* Authored by: Ana Gelez -* Authored by: Alessandro "Alecaddd" Castellani -*/ - -/** -* A digit input with a label next to it. -*/ -public class Akira.Partials.LinkedInput : Gtk.Grid { + * 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: Ana Gelez + * Authored by: Alessandro "Alecaddd" Castellani + */ + +/* + * A digit input with a label next to it. + */ +public class Akira.Widgets.LinkedInput : Gtk.Grid { public string label { get; construct set; } public string tooltip { get; construct set; } - public Akira.Partials.InputField input_field { get; construct set; } + public InputField input_field { get; construct set; } /** * Indicates whether the label or the entry should be first @@ -95,7 +95,7 @@ public class Akira.Partials.LinkedInput : Gtk.Grid { break; } - input_field = new Akira.Partials.InputField (icon, 7, true, false); + input_field = new Widgets.InputField (icon, 7, true, false); bind_property ( "value", input_field.entry, "value", BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE); diff --git a/src/Partials/MenuButton.vala b/src/Widgets/MenuButton.vala similarity index 53% rename from src/Partials/MenuButton.vala rename to src/Widgets/MenuButton.vala index ade89635f..9eb861b6b 100644 --- a/src/Partials/MenuButton.vala +++ b/src/Widgets/MenuButton.vala @@ -1,29 +1,28 @@ /* -* Copyright (c) 2019 Alecaddd (http://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: Alessandro "Alecaddd" Castellani -*/ - -public class Akira.Partials.MenuButton : Gtk.Grid { - + * 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: Alessandro "Alecaddd" Castellani + */ + +public class Akira.Widgets.MenuButton : Gtk.Grid { public Gtk.MenuButton button; private Gtk.Label label_btn; - public Akira.Partials.ButtonImage image; + public ButtonImage image; public MenuButton (string icon_name, string name, string[]? accels = null) { label_btn = new Gtk.Label (name); diff --git a/src/Widgets/PanelSeparator.vala b/src/Widgets/PanelSeparator.vala new file mode 100644 index 000000000..04e9260e6 --- /dev/null +++ b/src/Widgets/PanelSeparator.vala @@ -0,0 +1,28 @@ +/* + * 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: Alessandro "Alecaddd" Castellani + */ + +public class Akira.Widgets.PanelSeparator : Gtk.Separator { + construct { + orientation = Gtk.Orientation.HORIZONTAL; + get_style_context ().add_class ("panel-separator"); + margin_bottom = 6; + } +} diff --git a/src/Partials/RoundedColorButton.vala b/src/Widgets/RoundedColorButton.vala similarity index 97% rename from src/Partials/RoundedColorButton.vala rename to src/Widgets/RoundedColorButton.vala index 2ba66a0d1..7b82d3d99 100644 --- a/src/Partials/RoundedColorButton.vala +++ b/src/Widgets/RoundedColorButton.vala @@ -7,10 +7,12 @@ * 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 . * @@ -18,7 +20,7 @@ * Authored by: Alessandro "alecaddd" Castellani */ -public class Akira.Partials.RoundedColorButton : Gtk.Grid { +public class Akira.Widgets.RoundedColorButton : Gtk.Grid { public signal void set_color (string color); public RoundedColorButton (string color) { diff --git a/src/Partials/ZoomButton.vala b/src/Widgets/ZoomButton.vala similarity index 99% rename from src/Partials/ZoomButton.vala rename to src/Widgets/ZoomButton.vala index 64f7be42c..5d0fce1d6 100644 --- a/src/Partials/ZoomButton.vala +++ b/src/Widgets/ZoomButton.vala @@ -7,19 +7,19 @@ * 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: Alessandro "Alecaddd" Castellani */ -public class Akira.Partials.ZoomButton : Gtk.Grid { +public class Akira.Widgets.ZoomButton : Gtk.Grid { public weak Akira.Window window { get; set construct; } private Gtk.Label label_btn; diff --git a/src/meson.build b/src/meson.build index 1a674a915..e52fb30e5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -55,18 +55,18 @@ sources = files( 'Layouts/Partials/BorderRadiusPanel.vala', 'Layouts/Partials/AlignItemsPanel.vala', - 'Partials/AddColorButton.vala', - 'Partials/HeaderBarButton.vala', - 'Partials/MenuButton.vala', - 'Partials/ZoomButton.vala', - 'Partials/ButtonImage.vala', - 'Partials/AlignBoxButton.vala', - 'Partials/InputField.vala', - 'Partials/ColorField.vala', - 'Partials/LinkedInput.vala', - 'Partials/PanelSeparator.vala', - 'Partials/RoundedColorButton.vala', - 'Partials/ExportWidget.vala', + 'Widgets/AddColorButton.vala', + 'Widgets/AlignBoxButton.vala', + 'Widgets/ButtonImage.vala', + 'Widgets/ColorField.vala', + 'Widgets/ExportWidget.vala', + 'Widgets/HeaderBarButton.vala', + 'Widgets/InputField.vala', + 'Widgets/LinkedInput.vala', + 'Widgets/MenuButton.vala', + 'Widgets/PanelSeparator.vala', + 'Widgets/RoundedColorButton.vala', + 'Widgets/ZoomButton.vala', 'Models/BordersItemModel.vala', 'Models/FillsItemModel.vala', From e69ed33581a14ce13502020e34d7121cab1c9ece Mon Sep 17 00:00:00 2001 From: Alessandro Date: Sun, 6 Jun 2021 09:53:50 -0700 Subject: [PATCH 03/14] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 383b57eb7..61e846b4f 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ ninja && sudo ninja install ## 🤔 Questions If you want to ask any questions about the project, join us on our public Matrix channel at -[#akiraux:matrix.org](https://matrix.to/#/!kpfeTRbpocQrOFCFnJ:matrix.org) or on IRC at #akiraux on Freenode. +[#akiraux:matrix.org](https://matrix.to/#/#akiraux:matrix.org) or on IRC at #akiraux on LiberaChat. We also have a dedicated Discord channel available to any [Patreon](https://www.patreon.com/akiraux) supporters where we offer more direct support, and we're open to discussing new features and implementations. From 5bf10e4f51eb36bdd70023b4261dfe85f8a9b039 Mon Sep 17 00:00:00 2001 From: Alessandro Date: Sat, 12 Jun 2021 14:07:13 -0700 Subject: [PATCH 04/14] Add color row widget (#604) * Move color picker code to its own class * Move color field inside reusable color picker widget * Move the opacity field in the color button widget * Update the item fill color when the opacity field is changed * Properly update colors and alpha of items and UI buttons * Use a shared ColorModel for fills and borders * Reload list of fill deletion * Remove 2 way bindings for color fields * Hook border to reusable UI widget --- src/Layouts/Partials/BorderItem.vala | 242 ++--------------- src/Layouts/Partials/BordersPanel.vala | 45 ++-- src/Layouts/Partials/FillItem.vala | 320 +---------------------- src/Layouts/Partials/FillsPanel.vala | 51 ++-- src/Lib/Components/Fill.vala | 7 + src/Models/BaseModel.vala | 25 -- src/Models/BordersItemModel.vala | 70 ----- src/Models/ColorModel.vala | 92 +++++++ src/Models/FillsItemModel.vala | 65 ----- src/Utils/ColorPicker.vala | 4 +- src/Widgets/ColorField.vala | 14 +- src/Widgets/ColorRow.vala | 343 +++++++++++++++++++++++++ src/meson.build | 7 +- 13 files changed, 531 insertions(+), 754 deletions(-) delete mode 100644 src/Models/BaseModel.vala delete mode 100644 src/Models/BordersItemModel.vala create mode 100644 src/Models/ColorModel.vala delete mode 100644 src/Models/FillsItemModel.vala create mode 100644 src/Widgets/ColorRow.vala diff --git a/src/Layouts/Partials/BorderItem.vala b/src/Layouts/Partials/BorderItem.vala index 448718b68..fdac3924a 100644 --- a/src/Layouts/Partials/BorderItem.vala +++ b/src/Layouts/Partials/BorderItem.vala @@ -21,170 +21,44 @@ */ public class Akira.Layouts.Partials.BorderItem : Gtk.Grid { - public weak Akira.Window window { get; construct; } + private unowned Akira.Window window; + private unowned Lib.Components.Border border; - private Gtk.Grid color_chooser; - private Gtk.Button eyedropper_button; private Gtk.Button hidden_button; private Gtk.Button delete_button; private Gtk.Image hidden_button_icon; - private Gtk.Button selected_color; - public Widgets.InputField tickness_container; - public Widgets.ColorField color_container; - private Gtk.Popover color_popover; - private Gtk.Grid color_picker; - private Akira.Utils.ColorPicker eyedropper; - private Gtk.ColorChooserWidget? color_chooser_widget = null; - - public Akira.Models.BordersItemModel model { get; construct; } - - private string old_color; - - // If the color is manually set from the ColorPicker. - // If true, the ColorChooserWidget doesn't need to be updated. - private bool color_set_manually = false; - private string color { - owned get { - return model.color; - } set { - model.color = value; - } - } - - private int alpha { - owned get { - return model.alpha; - } set { - model.alpha = value; - } - } - - private int border_size { - owned get { - return model.size; - } set { - model.size = value; - } - } private bool hidden { owned get { - return model.hidden; + return border.hidden; } set { - model.hidden = value; + border.hidden = value; set_hidden_button (); toggle_ui_visibility (); } } - public BorderItem (Akira.Window window, Akira.Models.BordersItemModel model) { - Object ( - window: window, - model: model - ); - } + public signal void border_deleted (); - construct { - create_ui (); + public BorderItem (Akira.Window window, Lib.Components.Border border) { + this.window = window; + this.border = border; - // Update view BEFORE event bindings in order - // to not trigger bindings on first assignment. - update_view (); + create_ui (); + hidden = border.hidden; create_event_bindings (); show_all (); } - private void update_view () { - hidden = model.hidden; - color = model.color; - old_color = color; - } - private void create_ui () { margin_top = margin_bottom = 5; - color_chooser = new Gtk.Grid (); - color_chooser.hexpand = true; - color_chooser.margin_end = 5; - - var selected_color_container = new Gtk.Grid (); - var context = selected_color_container.get_style_context (); - context.add_class ("selected-color-container"); - context.add_class ("bg-pattern"); - - selected_color = new Gtk.Button (); - selected_color.vexpand = true; - selected_color.width_request = 40; - selected_color.can_focus = false; - selected_color.get_style_context ().add_class ("selected-color"); - selected_color.set_tooltip_text (_("Choose border color")); - - color_popover = new Gtk.Popover (selected_color); - color_popover.position = Gtk.PositionType.BOTTOM; - - selected_color.clicked.connect (() => { - init_color_chooser (); - color_popover.popup (); - }); - - selected_color_container.add (selected_color); - set_button_color (); - - eyedropper_button = new Gtk.Button (); - eyedropper_button.get_style_context ().add_class ("color-picker-button"); - eyedropper_button.can_focus = false; - eyedropper_button.valign = Gtk.Align.CENTER; - eyedropper_button.set_tooltip_text (_("Pick color")); - eyedropper_button.add (new Gtk.Image.from_icon_name ("color-select-symbolic", - Gtk.IconSize.SMALL_TOOLBAR)); - - var picker_container = new Gtk.Grid (); - picker_container.margin_end = 10; - picker_container.margin_top = picker_container.margin_bottom = 1; - picker_container.add (selected_color_container); - picker_container.add (eyedropper_button); + var fill_chooser = new Gtk.Grid (); + fill_chooser.hexpand = true; + fill_chooser.margin_end = 5; - color_container = new Widgets.ColorField (window); - color_container.text = Utils.Color.rgba_to_hex (color); - - model.bind_property ( - "color", color_container, "text", - BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE, - // model => this - (binding, model_value, ref color_container_value) => { - var model_rgba = model_value.dup_string (); - old_color = model_rgba; - color_container_value.set_string (Utils.Color.rgba_to_hex (model_rgba)); - return true; - }, - // this => model - (binding, color_container_value, ref model_value) => { - color_set_manually = false; - var color_container_hex = color_container_value.dup_string (); - if (!Utils.Color.is_valid_hex (color_container_hex)) { - model_value.set_string (Utils.Color.rgba_to_hex (old_color)); - return false; - } - var new_color_rgba = Utils.Color.hex_to_rgba (color_container_hex); - new_color_rgba.alpha = alpha / 100; - model_value.set_string (new_color_rgba.to_string ()); - return true; - } - ); - - tickness_container = new Widgets.InputField ( - Widgets.InputField.Unit.PIXEL, 7, true, true); - tickness_container.set_range (0, Akira.Layouts.MainCanvas.CANVAS_SIZE / 2); - tickness_container.entry.sensitive = true; - tickness_container.entry.value = border_size; - - tickness_container.entry.bind_property ( - "value", model, "size", BindingFlags.BIDIRECTIONAL); - - color_chooser.attach (picker_container, 0, 0, 1, 1); - color_chooser.attach (color_container, 1, 0, 1, 1); - color_chooser.attach (tickness_container, 2, 0, 1, 1); + fill_chooser.add (new Widgets.ColorRow (window, new Models.ColorModel (null, border))); hidden_button = new Gtk.Button (); hidden_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); @@ -197,66 +71,23 @@ public class Akira.Layouts.Partials.BorderItem : Gtk.Grid { delete_button.get_style_context ().add_class ("button-rounded"); delete_button.can_focus = false; delete_button.valign = Gtk.Align.CENTER; - delete_button.set_tooltip_text (_("Remove border")); + delete_button.set_tooltip_text (_("Remove border color")); delete_button.add (new Gtk.Image.from_icon_name ("user-trash-symbolic", Gtk.IconSize.SMALL_TOOLBAR)); - attach (color_chooser, 0, 0, 1, 1); + attach (fill_chooser, 0, 0, 1, 1); attach (hidden_button, 1, 0, 1, 1); attach (delete_button, 2, 0, 1, 1); } - private void init_color_chooser () { - if (color_chooser_widget != null) { - return; - } - - color_chooser_widget = new Gtk.ColorChooserWidget (); - color_chooser_widget.hexpand = true; - color_chooser_widget.show_editor = true; - - color_picker = new Gtk.Grid (); - color_picker.get_style_context ().add_class ("color-picker"); - color_picker.attach (color_chooser_widget, 0, 0, 1, 1); - color_picker.show_all (); - color_popover.add (color_picker); - - set_color_chooser_color (); - color_chooser_widget.notify["rgba"].connect (on_color_changed); - } - private void create_event_bindings () { - eyedropper_button.clicked.connect (on_eyedropper_click); delete_button.clicked.connect (on_delete_item); hidden_button.clicked.connect (toggle_visibility); } - private void on_eyedropper_click () { - eyedropper = new Akira.Utils.ColorPicker (); - eyedropper.show_all (); - - eyedropper.picked.connect ((picked_color) => { - init_color_chooser (); - color_chooser_widget.set_rgba (picked_color); - eyedropper.close (); - }); - - eyedropper.cancelled.connect (() => { - eyedropper.close (); - }); - } - - private void on_color_changed () { - color_set_manually = true; - color = color_chooser_widget.rgba.to_string (); - alpha = ((int)(color_chooser_widget.rgba.alpha * 255)); - set_button_color (); - } - private void on_delete_item () { - model.model.remove_item.begin (model); - // Actually remove the Border component only if the user requests it. - model.border.remove (); + border.remove (); + border_deleted (); } private void set_hidden_button () { @@ -287,41 +118,4 @@ public class Akira.Layouts.Partials.BorderItem : Gtk.Grid { hidden_button.set_tooltip_text (_("Hide border")); get_style_context ().remove_class ("disabled"); } - - private void set_button_color () { - try { - var provider = new Gtk.CssProvider (); - var context = selected_color.get_style_context (); - - var new_rgba = Gdk.RGBA (); - new_rgba.parse (color); - new_rgba.alpha = (double) alpha / 255; - var new_color = new_rgba.to_string (); - - var css = """.selected-color { - background-color: %s; - border-color: shade (%s, 0.75); - }""".printf (new_color, new_color); - - provider.load_from_data (css, css.length); - - context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); - } catch (Error e) { - warning ("Style error: %s", e.message); - } - } - - private void set_color_chooser_color () { - // Prevent infinite loop by checking whether the color - // has been set manually or not - if (color_set_manually) { - return; - } - - var new_rgba = Gdk.RGBA (); - new_rgba.parse (color); - new_rgba.alpha = (double) alpha / 255; - - color_chooser_widget.set_rgba (new_rgba); - } } diff --git a/src/Layouts/Partials/BordersPanel.vala b/src/Layouts/Partials/BordersPanel.vala index d693c440c..e193668b4 100644 --- a/src/Layouts/Partials/BordersPanel.vala +++ b/src/Layouts/Partials/BordersPanel.vala @@ -20,11 +20,11 @@ */ public class Akira.Layouts.Partials.BordersPanel : Gtk.Grid { - public weak Akira.Window window { get; construct; } + private unowned Akira.Window window; public Gtk.Button add_btn; public Gtk.ListBox borders_list_container; - public Akira.Models.ListModel list_model; + private GLib.ListStore list; private unowned List? items; public bool toggled { @@ -37,13 +37,8 @@ public class Akira.Layouts.Partials.BordersPanel : Gtk.Grid { } public BordersPanel (Akira.Window window) { - Object ( - window: window, - orientation: Gtk.Orientation.HORIZONTAL - ); - } + this.window = window; - construct { var title_cont = new Gtk.Grid (); title_cont.orientation = Gtk.Orientation.HORIZONTAL; title_cont.hexpand = true; @@ -66,7 +61,7 @@ public class Akira.Layouts.Partials.BordersPanel : Gtk.Grid { title_cont.attach (label, 0, 0, 1, 1); title_cont.attach (add_btn, 1, 0, 1, 1); - list_model = new Akira.Models.ListModel (); + list = new GLib.ListStore (typeof (Lib.Components.Border)); borders_list_container = new Gtk.ListBox (); borders_list_container.margin_top = 5; @@ -76,8 +71,12 @@ public class Akira.Layouts.Partials.BordersPanel : Gtk.Grid { borders_list_container.selection_mode = Gtk.SelectionMode.NONE; borders_list_container.get_style_context ().add_class ("fills-list"); - borders_list_container.bind_model (list_model, item => { - return new Akira.Layouts.Partials.BorderItem (window, (Akira.Models.BordersItemModel) item); + borders_list_container.bind_model (list, item => { + var border_items = new Layouts.Partials.BorderItem (window, (Lib.Components.Border) item); + border_items.border_deleted.connect (() => { + reload_list (items); + }); + return border_items; }); attach (title_cont, 0, 0, 1, 1); @@ -89,38 +88,35 @@ public class Akira.Layouts.Partials.BordersPanel : Gtk.Grid { private void create_event_bindings () { toggled = false; - window.event_bus.selected_items_list_changed.connect (on_selected_items_list_changed); + window.event_bus.selected_items_list_changed.connect (reload_list); add_btn.clicked.connect (() => { var border_color = Gdk.RGBA (); border_color.parse (settings.border_color); foreach (Lib.Items.CanvasItem item in items) { - Lib.Components.Border border = item.borders.add_border_color (border_color, (int) settings.border_size); - var model_item = create_model (border); - list_model.add_item.begin (model_item); + list.insert (0, item.borders.add_border_color (border_color, (int) settings.border_size)); } }); // Listen to the model changes when adding/removing items. - list_model.items_changed.connect ((position, removed, added) => { + list.items_changed.connect ((position, removed, added) => { window.main_window.left_sidebar.queue_resize (); }); } - private void on_selected_items_list_changed (List selected_items) { + private void reload_list (List selected_items) { + // Always clear the list model when a selection changes. + list.remove_all (); + if (selected_items.length () == 0) { items = null; - list_model.clear.begin (); toggled = false; return; } items = selected_items; - // Always clear the list model when a selection changes. - list_model.clear.begin (); - bool show = false; foreach (Lib.Items.CanvasItem item in selected_items) { // Skip items that don't have a border item since there will be nothing to show. @@ -134,15 +130,10 @@ public class Akira.Layouts.Partials.BordersPanel : Gtk.Grid { // Loops through all the available borders and add them tot he list model. // TODO: handle duplicate identical colors. foreach (Lib.Components.Border border in item.borders.borders) { - var model_item = create_model (border); - list_model.add_item.begin (model_item); + list.insert (0, border); } } toggled = show; } - - private Akira.Models.BordersItemModel create_model (Lib.Components.Border border) { - return new Akira.Models.BordersItemModel (border, list_model); - } } diff --git a/src/Layouts/Partials/FillItem.vala b/src/Layouts/Partials/FillItem.vala index a18d7799c..f7bf349ce 100644 --- a/src/Layouts/Partials/FillItem.vala +++ b/src/Layouts/Partials/FillItem.vala @@ -22,182 +22,44 @@ */ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { - public weak Akira.Window window { get; construct; } + private unowned Akira.Window window; + private unowned Lib.Components.Fill fill; - private Gtk.Grid fill_chooser; - private Gtk.Button eyedropper_button; private Gtk.Button hidden_button; private Gtk.Button delete_button; private Gtk.Image hidden_button_icon; - private Gtk.Button selected_color; - private Widgets.InputField opacity_container; - private Widgets.ColorField color_container; - private Gtk.Popover color_popover; - private Gtk.Grid color_picker; - private Gtk.ColorChooserWidget? color_chooser_widget = null; - private Akira.Utils.ColorPicker eyedropper; - - private Gtk.FlowBox global_colors_flowbox; - - public Akira.Models.FillsItemModel model { get; construct; } - - /* - * 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. - */ - private enum Container { - GLOBAL, - DOCUMENT - } - - private string old_color; - - // If the color or alpha are manually set from the ColorPicker. - // If true, the ColorChooserWidget doesn't need to be updated. - private bool color_set_manually = false; - private string color { - owned get { - return model.color; - } set { - model.color = value; - } - } - - private int alpha { - owned get { - return model.alpha; - } set { - model.alpha = value; - } - } private bool hidden { owned get { - return model.hidden; + return fill.hidden; } set { - model.hidden = value; + fill.hidden = value; set_hidden_button (); toggle_ui_visibility (); } } - public FillItem (Akira.Window window, Akira.Models.FillsItemModel model) { - Object ( - window: window, - model: model - ); - } + public signal void fill_deleted (); - construct { - create_ui (); + public FillItem (Akira.Window window, Lib.Components.Fill fill) { + this.window = window; + this.fill = fill; - // Update view BEFORE event bindings in order - // to not trigger bindings on first assignment. - update_view (); + create_ui (); + hidden = fill.hidden; create_event_bindings (); show_all (); } - private void update_view () { - hidden = model.hidden; - color = model.color; - old_color = color; - } - private void create_ui () { margin_top = margin_bottom = 5; - fill_chooser = new Gtk.Grid (); + var fill_chooser = new Gtk.Grid (); fill_chooser.hexpand = true; fill_chooser.margin_end = 5; - var selected_color_container = new Gtk.Grid (); - var context = selected_color_container.get_style_context (); - context.add_class ("selected-color-container"); - context.add_class ("bg-pattern"); - - selected_color = new Gtk.Button (); - selected_color.vexpand = true; - selected_color.width_request = 40; - selected_color.can_focus = false; - selected_color.get_style_context ().add_class ("selected-color"); - selected_color.set_tooltip_text (_("Choose fill color")); - - color_popover = new Gtk.Popover (selected_color); - color_popover.position = Gtk.PositionType.BOTTOM; - - selected_color.clicked.connect (() => { - init_color_chooser (); - color_popover.popup (); - }); - - selected_color_container.add (selected_color); - set_button_color (); - - eyedropper_button = new Gtk.Button (); - eyedropper_button.get_style_context ().add_class ("color-picker-button"); - eyedropper_button.can_focus = false; - eyedropper_button.valign = Gtk.Align.CENTER; - eyedropper_button.set_tooltip_text (_("Pick color")); - eyedropper_button.add (new Gtk.Image.from_icon_name ("color-select-symbolic", - Gtk.IconSize.SMALL_TOOLBAR)); - - var picker_container = new Gtk.Grid (); - picker_container.margin_end = 10; - picker_container.margin_top = picker_container.margin_bottom = 1; - picker_container.add (selected_color_container); - picker_container.add (eyedropper_button); - - color_container = new Widgets.ColorField (window); - color_container.text = Utils.Color.rgba_to_hex (color); - - model.bind_property ( - "color", color_container, "text", - BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE, - // model => this - (binding, model_value, ref color_container_value) => { - var model_rgba = model_value.dup_string (); - old_color = model_rgba; - color_container_value.set_string (Utils.Color.rgba_to_hex (model_rgba)); - return true; - }, - // this => model - (binding, color_container_value, ref model_value) => { - color_set_manually = false; - var color_container_hex = color_container_value.dup_string (); - if (!Utils.Color.is_valid_hex (color_container_hex)) { - model_value.set_string (Utils.Color.rgba_to_hex (old_color)); - return false; - } - var new_color_rgba = Utils.Color.hex_to_rgba (color_container_hex); - new_color_rgba.alpha = alpha / 100; - model_value.set_string (new_color_rgba.to_string ()); - return true; - } - ); - - opacity_container = new Widgets.InputField ( - Widgets.InputField.Unit.PERCENTAGE, 7, true, true); - opacity_container.entry.sensitive = true; - opacity_container.entry.value = Math.round ((double) alpha / 255 * 100); - - opacity_container.entry.bind_property ( - "value", model, "alpha", - BindingFlags.BIDIRECTIONAL, - (binding, srcval, ref targetval) => { - color_set_manually = false; - targetval.set_int ((int) ((double) srcval / 100 * 255)); - return true; - }, - (binding, srcval, ref targetval) => { - targetval.set_double ((srcval.get_int () * 100) / 255); - return true; - }); - - fill_chooser.attach (picker_container, 0, 0, 1, 1); - fill_chooser.attach (color_container, 1, 0, 1, 1); - fill_chooser.attach (opacity_container, 2, 0, 1, 1); + fill_chooser.add (new Widgets.ColorRow (window, new Models.ColorModel (fill))); hidden_button = new Gtk.Button (); hidden_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); @@ -219,127 +81,14 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { attach (delete_button, 2, 0, 1, 1); } - private void init_color_chooser () { - if (color_chooser_widget != null) { - return; - } - - color_chooser_widget = new Gtk.ColorChooserWidget (); - color_chooser_widget.hexpand = true; - color_chooser_widget.show_editor = true; - - color_picker = new Gtk.Grid (); - color_picker.get_style_context ().add_class ("color-picker"); - color_picker.row_spacing = 12; - - var global_colors_label = new Gtk.Label (_("Global colors")); - global_colors_label.halign = Gtk.Align.START; - global_colors_label.margin_start = global_colors_label.margin_end = 6; - - global_colors_flowbox = new Gtk.FlowBox (); - global_colors_flowbox.get_style_context ().add_class ("color-grid"); - global_colors_flowbox.selection_mode = Gtk.SelectionMode.NONE; - global_colors_flowbox.homogeneous = false; - global_colors_flowbox.column_spacing = global_colors_flowbox.row_spacing = 6; - global_colors_flowbox.margin_start = global_colors_flowbox.margin_end = 6; - // Large number to allow children to spread out the available space. - global_colors_flowbox.max_children_per_line = 100; - global_colors_flowbox.set_sort_func (sort_colors_function); - - var add_global_color_btn = new Widgets.AddColorButton (); - add_global_color_btn.clicked.connect (() => { - on_save_color (Container.GLOBAL); - }); - global_colors_flowbox.add (add_global_color_btn); - - foreach (string color in settings.global_colors) { - var btn = create_color_button (color); - global_colors_flowbox.add (btn); - } - - color_picker.attach (color_chooser_widget, 0, 0, 1, 1); - color_picker.attach (global_colors_label, 0, 1, 1, 1); - color_picker.attach (global_colors_flowbox, 0, 2, 1, 1); - color_picker.show_all (); - color_popover.add (color_picker); - - set_color_chooser_color (); - color_chooser_widget.notify["rgba"].connect (on_color_changed); - } - private void create_event_bindings () { - eyedropper_button.clicked.connect (on_eyedropper_click); delete_button.clicked.connect (on_delete_item); hidden_button.clicked.connect (toggle_visibility); } - /* - * Add the current color to the parent flowbox. - */ - private void on_save_color (Container parent) { - // Store the currently active color. - var color = color_chooser_widget.rgba.to_string (); - - // Create the new color button and connect to its signal. - var btn = create_color_button (color); - - // Update the colors list and the schema based on the colors container. - switch (parent) { - case Container.GLOBAL: - global_colors_flowbox.add (btn); - var array = settings.global_colors; - array += color; - settings.global_colors = array; - break; - - case Container.DOCUMENT: - // TODO... - break; - } - } - - private Gtk.FlowBoxChild create_color_button (string color) { - var child = new Gtk.FlowBoxChild (); - child.valign = child.halign = Gtk.Align.CENTER; - - var btn = new Widgets.RoundedColorButton (color); - btn.set_color.connect ((color) => { - var rgba_color = Gdk.RGBA (); - rgba_color.parse (color); - color_chooser_widget.set_rgba (rgba_color); - }); - - child.add (btn); - child.show_all (); - return child; - } - - private void on_eyedropper_click () { - eyedropper = new Akira.Utils.ColorPicker (); - eyedropper.show_all (); - - eyedropper.picked.connect ((picked_color) => { - init_color_chooser (); - color_chooser_widget.set_rgba (picked_color); - eyedropper.close (); - }); - - eyedropper.cancelled.connect (() => { - eyedropper.close (); - }); - } - - private void on_color_changed () { - color_set_manually = true; - color = color_chooser_widget.rgba.to_string (); - alpha = ((int)(color_chooser_widget.rgba.alpha * 255)); - set_button_color (); - } - private void on_delete_item () { - model.model.remove_item.begin (model); - // Actually remove the Fill component only if the user requests it. - model.fill.remove (); + fill.remove (); + fill_deleted (); } private void set_hidden_button () { @@ -370,45 +119,4 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { hidden_button.set_tooltip_text (_("Hide fill color")); get_style_context ().remove_class ("disabled"); } - - private void set_button_color () { - try { - var provider = new Gtk.CssProvider (); - var context = selected_color.get_style_context (); - - var new_rgba = Gdk.RGBA (); - new_rgba.parse (color); - new_rgba.alpha = (double) alpha / 255; - var new_color = new_rgba.to_string (); - - var css = """.selected-color { - background-color: %s; - border-color: shade (%s, 0.75); - }""".printf (new_color, new_color); - - provider.load_from_data (css, css.length); - - context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); - } catch (Error e) { - warning ("Style error: %s", e.message); - } - } - - private void set_color_chooser_color () { - // Prevent infinite loop by checking whether the color - // has been set manually or not - if (color_set_manually) { - return; - } - - var new_rgba = Gdk.RGBA (); - new_rgba.parse (color); - new_rgba.alpha = (double) alpha / 255; - - color_chooser_widget.set_rgba (new_rgba); - } - - private int sort_colors_function (Gtk.FlowBoxChild a, Gtk.FlowBoxChild b) { - return (a is Widgets.AddColorButton) ? -1 : 1; - } } diff --git a/src/Layouts/Partials/FillsPanel.vala b/src/Layouts/Partials/FillsPanel.vala index 02056993e..f05344aea 100644 --- a/src/Layouts/Partials/FillsPanel.vala +++ b/src/Layouts/Partials/FillsPanel.vala @@ -21,11 +21,11 @@ */ public class Akira.Layouts.Partials.FillsPanel : Gtk.Grid { - public weak Akira.Window window { get; construct; } + private unowned Akira.Window window; - public Gtk.Button add_btn; - public Gtk.ListBox fills_list_container; - public Akira.Models.ListModel list_model; + private Gtk.Button add_btn; + private Gtk.ListBox fills_list_container; + private GLib.ListStore list; private unowned List? items; public bool toggled { @@ -38,13 +38,8 @@ public class Akira.Layouts.Partials.FillsPanel : Gtk.Grid { } public FillsPanel (Akira.Window window) { - Object ( - window: window, - orientation: Gtk.Orientation.HORIZONTAL - ); - } + this.window = window; - construct { var title_cont = new Gtk.Grid (); title_cont.orientation = Gtk.Orientation.HORIZONTAL; title_cont.hexpand = true; @@ -67,7 +62,7 @@ public class Akira.Layouts.Partials.FillsPanel : Gtk.Grid { title_cont.attach (label, 0, 0, 1, 1); title_cont.attach (add_btn, 1, 0, 1, 1); - list_model = new Akira.Models.ListModel (); + list = new GLib.ListStore (typeof (Lib.Components.Fill)); fills_list_container = new Gtk.ListBox (); fills_list_container.margin_top = 5; @@ -77,8 +72,12 @@ public class Akira.Layouts.Partials.FillsPanel : Gtk.Grid { fills_list_container.selection_mode = Gtk.SelectionMode.NONE; fills_list_container.get_style_context ().add_class ("fills-list"); - fills_list_container.bind_model (list_model, item => { - return new Akira.Layouts.Partials.FillItem (window, (Akira.Models.FillsItemModel) item); + fills_list_container.bind_model (list, item => { + var fill_item = new Layouts.Partials.FillItem (window, (Lib.Components.Fill) item); + fill_item.fill_deleted.connect (() => { + reload_list (items); + }); + return fill_item; }); attach (title_cont, 0, 0, 1, 1); @@ -90,38 +89,35 @@ public class Akira.Layouts.Partials.FillsPanel : Gtk.Grid { private void create_event_bindings () { toggled = false; - window.event_bus.selected_items_list_changed.connect (on_selected_items_list_changed); + window.event_bus.selected_items_list_changed.connect (reload_list); add_btn.clicked.connect (() => { var fill_color = Gdk.RGBA (); fill_color.parse (settings.fill_color); foreach (Lib.Items.CanvasItem item in items) { - Lib.Components.Fill fill = item.fills.add_fill_color (fill_color); - var model_item = create_model (fill); - list_model.add_item.begin (model_item); + list.insert (0, item.fills.add_fill_color (fill_color)); } }); - // Listen to the model changes when adding/removing items. - list_model.items_changed.connect ((position, removed, added) => { + // Listen to the model changes when adding or removing items. + list.items_changed.connect ((position, removed, added) => { window.main_window.left_sidebar.queue_resize (); }); } - private void on_selected_items_list_changed (List selected_items) { + private void reload_list (List selected_items) { + // Always clear the list model when a selection changes. + list.remove_all (); + if (selected_items.length () == 0) { items = null; - list_model.clear.begin (); toggled = false; return; } items = selected_items; - // Always clear the list model when a selection changes. - list_model.clear.begin (); - bool show = false; foreach (Lib.Items.CanvasItem item in selected_items) { // Skip items that don't have a fill item since there will be nothing to show. @@ -135,15 +131,10 @@ public class Akira.Layouts.Partials.FillsPanel : Gtk.Grid { // Loops through all the available fills and add them tot he list model. // TODO: handle duplicate identical colors. foreach (Lib.Components.Fill fill in item.fills.fills) { - var model_item = create_model (fill); - list_model.add_item.begin (model_item); + list.insert (0, fill); } } toggled = show; } - - private Akira.Models.FillsItemModel create_model (Lib.Components.Fill fill) { - return new Akira.Models.FillsItemModel (fill, list_model); - } } diff --git a/src/Lib/Components/Fill.vala b/src/Lib/Components/Fill.vala index 91286e728..6bdeabdee 100644 --- a/src/Lib/Components/Fill.vala +++ b/src/Lib/Components/Fill.vala @@ -52,6 +52,13 @@ public class Akira.Lib.Components.Fill : Component { this.notify["hidden"].connect (() => { fills.reload (); }); + + this.notify["alpha"].connect (() => { + var rgba = Gdk.RGBA (); + rgba = color; + rgba.alpha = ((double) alpha) / 255; + color = rgba; + }); } public void remove () { diff --git a/src/Models/BaseModel.vala b/src/Models/BaseModel.vala deleted file mode 100644 index 1703fe36e..000000000 --- a/src/Models/BaseModel.vala +++ /dev/null @@ -1,25 +0,0 @@ -/* -* Copyright (c) 2020 Alecaddd (https://alecaddd.com) -* -* This file is part of Akira. -* -* Akira is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* Akira is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* You should have received a copy of the GNU General Public License -* along with Akira. If not, see . -* -* Authored by: Alessandro "Alecaddd" Castellani -* Authored by: Giacomo "giacomoalbe" Alberini -*/ - -public abstract class Akira.Models.BaseModel : GLib.Object { - public Lib.Components.Fill fill { get; set; } - public Lib.Components.Border border { get; set; } - public Akira.Models.ListModel model { get; set; } -} diff --git a/src/Models/BordersItemModel.vala b/src/Models/BordersItemModel.vala deleted file mode 100644 index 5c144904a..000000000 --- a/src/Models/BordersItemModel.vala +++ /dev/null @@ -1,70 +0,0 @@ -/** - * 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: Alessandro "alecaddd" Castellani - */ - -public class Akira.Models.BordersItemModel : Models.BaseModel { - public string color { - owned get { - return border.color.to_string (); - } - set { - var new_rgba = Gdk.RGBA (); - new_rgba.parse (value); - border.color = new_rgba; - } - } - - public int alpha { - get { - return border.alpha; - } - set { - border.alpha = value; - } - } - - public int size { - get { - return border.size; - } - set { - border.size = value; - } - } - - public bool hidden { - get { - return border.hidden; - } - set { - border.hidden = value; - } - } - - public BordersItemModel (Lib.Components.Border _border, ListModel _model) { - border = _border; - model = _model; - } - - public string to_string () { - return "Color: %s\nAlpha: %f\nSize: %i\nHidden: %s".printf ( - color, alpha, size, (hidden ? "1" : "0")); - } -} diff --git a/src/Models/ColorModel.vala b/src/Models/ColorModel.vala new file mode 100644 index 000000000..059fcce9a --- /dev/null +++ b/src/Models/ColorModel.vala @@ -0,0 +1,92 @@ +/* + * Copyright (c) 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: Alessandro "Alecaddd" Castellani + */ + +/* + * Model to keep track of Fills and Borders colors of an item. We use this + * model to easily bind the GtkListBox UI to the Fills and Borders Components. + */ +public class Akira.Models.ColorModel : GLib.Object { + private unowned Lib.Components.Fill? fill; + private unowned Lib.Components.Border? border; + + public Type type; + public enum Type { + FILL, + BORDER + } + + public string color { + owned get { + return type == Type.FILL ? fill.color.to_string () : border.color.to_string (); + } + set { + var new_rgba = Gdk.RGBA (); + new_rgba.parse (value); + new_rgba.alpha = (double) alpha / 255; + if (type == Type.FILL) { + fill.color = new_rgba; + return; + } + border.color = new_rgba; + } + } + + public int alpha { + get { + return type == Type.FILL ? fill.alpha : border.alpha; + } + set { + if (type == Type.FILL) { + fill.alpha = value; + return; + } + border.alpha = value; + } + } + + public bool hidden { + get { + return type == Type.FILL ? fill.hidden : border.hidden; + } + set { + if (type == Type.FILL) { + fill.hidden = value; + return; + } + border.hidden = value; + } + } + + public int size { + get { + return border.size; + } + set { + border.size = value; + } + } + + public ColorModel (Lib.Components.Fill? fill, Lib.Components.Border? border = null) { + type = fill != null ? Type.FILL : Type.BORDER; + this.fill = fill; + this.border = border; + } +} diff --git a/src/Models/FillsItemModel.vala b/src/Models/FillsItemModel.vala deleted file mode 100644 index 0dece9bd7..000000000 --- a/src/Models/FillsItemModel.vala +++ /dev/null @@ -1,65 +0,0 @@ -/** - * 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: Giacomo "giacomoalbe" Alberini - * Authored by: Alessandro "alecaddd" Castellani - */ - -public class Akira.Models.FillsItemModel : Models.BaseModel { - public string color { - owned get { - return fill.color.to_string (); - } - set { - var new_rgba = Gdk.RGBA (); - new_rgba.parse (value); - fill.color = new_rgba; - } - } - - public int alpha { - get { - return fill.alpha; - } - set { - fill.alpha = value; - } - } - - public bool hidden { - get { - return fill.hidden; - } - set { - fill.hidden = value; - } - } - - public Akira.Utils.BlendingMode blending_mode; - - public FillsItemModel (Lib.Components.Fill _fill, ListModel _model) { - fill = _fill; - model = _model; - blending_mode = Akira.Utils.BlendingMode.NORMAL; - } - - public string to_string () { - return "Color: %s\nAlpha: %f\nHidden: %s\nBlendingMode: %s".printf ( - color, alpha, (hidden ? "1" : "0"), blending_mode.to_string ()); - } -} diff --git a/src/Utils/ColorPicker.vala b/src/Utils/ColorPicker.vala index 10c2e86f2..249377052 100644 --- a/src/Utils/ColorPicker.vala +++ b/src/Utils/ColorPicker.vala @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with Akira. If not, see . -* +* * Authored by: Ivan "isneezy" Vilanculo * Ported from: https://github.com/ColorPicker/RonnyDo */ @@ -194,7 +194,7 @@ public class Akira.Utils.ColorPicker : Gtk.Window { } - // Draw an outside bright magnifier border + // Draw an outside bright magnifier border Gdk.cairo_set_source_rgba (base_context, bright_border_color); base_context.arc (radius + shadow_width, radius + shadow_width, radius - 1, 0, 2 * Math.PI); base_context.stroke (); diff --git a/src/Widgets/ColorField.vala b/src/Widgets/ColorField.vala index 3d16399f8..146f83430 100644 --- a/src/Widgets/ColorField.vala +++ b/src/Widgets/ColorField.vala @@ -25,7 +25,7 @@ public class Akira.Widgets.ColorField : Gtk.Entry { public ColorField (Akira.Window window) { this.window = window; - margin_end = 10; + margin_end = margin_start = 10; width_chars = 8; max_length = 7; hexpand = true; @@ -33,12 +33,14 @@ public class Akira.Widgets.ColorField : Gtk.Entry { focus_in_event.connect (handle_focus_in); focus_out_event.connect (handle_focus_out); insert_text.connect (handle_insert_text); + key_press_event.connect (handle_key_press); } ~ColorField () { focus_in_event.disconnect (handle_focus_in); focus_out_event.disconnect (handle_focus_out); insert_text.disconnect (handle_insert_text); + key_press_event.disconnect (handle_key_press); } private void handle_insert_text (string text, int length, ref int position) { @@ -87,4 +89,14 @@ public class Akira.Widgets.ColorField : Gtk.Entry { window.event_bus.connect_typing_accel (); return false; } + + private bool handle_key_press (Gdk.EventKey event) { + // Enter or Escape + if (event.keyval == Gdk.Key.Return || event.keyval == Gdk.Key.Escape) { + window.event_bus.set_focus_on_canvas (); + return true; + } + + return false; + } } diff --git a/src/Widgets/ColorRow.vala b/src/Widgets/ColorRow.vala new file mode 100644 index 000000000..2d39ac5f2 --- /dev/null +++ b/src/Widgets/ColorRow.vala @@ -0,0 +1,343 @@ +/* + * Copyright (c) 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: Alessandro "alecaddd" Castellani + */ + +/* + * Helper class to quickly create a container with a color button and a color + * picker. The color button opens up the GtkColorChooser. + */ +public class Akira.Widgets.ColorRow : Gtk.Grid { + private unowned Akira.Window window; + private unowned Models.ColorModel model; + + private Gtk.Button color_button; + private Gtk.Popover color_popover; + private Gtk.ColorChooserWidget? color_chooser_widget = null; + private Gtk.FlowBox global_colors_flowbox; + private ColorField field; + private InputField opacity_field; + + /* + * If the color or alpha are manually set from the ColorPicker. + * If true, the ColorChooserWidget doesn't need to be updated. + */ + private bool color_set_manually = false; + + /* + * Keep track of the current color when the user is updating the string + * and the format is not valid. + */ + private string old_color; + + /* + * 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. + */ + private enum Container { + GLOBAL, + DOCUMENT + } + + public ColorRow (Akira.Window window, Models.ColorModel model) { + this.window = window; + this.model = model; + + old_color = model.color; + + margin_top = margin_bottom = 1; + + var container = new Gtk.Grid (); + var context = container.get_style_context (); + context.add_class ("selected-color-container"); + context.add_class ("bg-pattern"); + + color_button = new Gtk.Button (); + color_button.vexpand = true; + color_button.width_request = 40; + color_button.can_focus = false; + color_button.get_style_context ().add_class ("selected-color"); + color_button.set_tooltip_text (_("Choose color")); + + color_popover = new Gtk.Popover (color_button); + color_popover.position = Gtk.PositionType.BOTTOM; + + color_button.clicked.connect (() => { + init_color_chooser (); + color_popover.popup (); + }); + + container.add (color_button); + set_button_color (model.color, model.alpha); + + // Define the eye dropper button. + var eyedropper_button = new Gtk.Button (); + eyedropper_button.get_style_context ().add_class ("color-picker-button"); + eyedropper_button.can_focus = false; + eyedropper_button.valign = Gtk.Align.CENTER; + eyedropper_button.set_tooltip_text (_("Pick color")); + eyedropper_button.add ( + new Gtk.Image.from_icon_name ("color-select-symbolic", + Gtk.IconSize.SMALL_TOOLBAR) + ); + eyedropper_button.clicked.connect (on_eyedropper_click); + + add (container); + add (eyedropper_button); + + field = new ColorField (window); + field.text = Utils.Color.rgba_to_hex (model.color); + field.changed.connect (() => { + // Don't do anything if the color change came from the chooser. + if (color_set_manually) { + return; + } + + var field_hex = field.text; + // Interrupt if what's written is not a valid color value. + if (!Utils.Color.is_valid_hex (field_hex)) { + return; + } + + // Since we will update the color picker, prevent an infinite loop. + color_set_manually = true; + + var new_rgba = Utils.Color.hex_to_rgba (field_hex); + model.color = new_rgba.to_string (); + set_button_color (field_hex, model.alpha); + + // Update the chooser widget only if it was already initialized. + if (color_chooser_widget != null) { + set_chooser_color (new_rgba.to_string (), model.alpha); + } + + // Reset the bool to allow edits from the color chooser. + color_set_manually = false; + }); + + add (field); + + // Show the opacity field if this widget was generated from the Fills list. + if (model.type == Models.ColorModel.Type.FILL) { + opacity_field = new InputField (InputField.Unit.PERCENTAGE, 7, true, true); + opacity_field.entry.sensitive = true; + opacity_field.entry.value = Math.round ((double) model.alpha / 255 * 100); + + opacity_field.entry.value_changed.connect (() => { + // Don't do anything if the color change came from the chooser. + if (color_set_manually) { + return; + } + + // Since we will update the color picker, prevent an infinite loop. + color_set_manually = true; + + var alpha = (int) ((double) opacity_field.entry.value / 100 * 255); + model.alpha = alpha; + set_button_color (model.color, alpha); + + // Update the chooser widget only if it was already initialized. + if (color_chooser_widget != null) { + set_chooser_color (model.color, alpha); + } + + // Reset the bool to allow edits from the color chooser. + color_set_manually = false; + }); + + add (opacity_field); + } + + // Show the border field if this widget was generated from the Borders list. + if (model.type == Models.ColorModel.Type.BORDER) { + var border = new InputField (InputField.Unit.PIXEL, 7, true, true); + border.set_range (0, Layouts.MainCanvas.CANVAS_SIZE / 2); + border.entry.sensitive = true; + border.entry.value = model.size; + border.entry.bind_property ("value", model, "size", BindingFlags.BIDIRECTIONAL); + + add (border); + } + } + + private void init_color_chooser () { + if (color_chooser_widget != null) { + return; + } + + color_chooser_widget = new Gtk.ColorChooserWidget (); + color_chooser_widget.hexpand = true; + color_chooser_widget.show_editor = true; + + var color_grid = new Gtk.Grid (); + color_grid.get_style_context ().add_class ("color-picker"); + color_grid.row_spacing = 12; + + var global_colors_label = new Gtk.Label (_("Global colors")); + global_colors_label.halign = Gtk.Align.START; + global_colors_label.margin_start = global_colors_label.margin_end = 6; + + global_colors_flowbox = new Gtk.FlowBox (); + global_colors_flowbox.get_style_context ().add_class ("color-grid"); + global_colors_flowbox.selection_mode = Gtk.SelectionMode.NONE; + global_colors_flowbox.homogeneous = false; + global_colors_flowbox.column_spacing = global_colors_flowbox.row_spacing = 6; + global_colors_flowbox.margin_start = global_colors_flowbox.margin_end = 6; + // Large number to allow children to spread out the available space. + global_colors_flowbox.max_children_per_line = 100; + global_colors_flowbox.set_sort_func (sort_colors_function); + + var add_global_color_btn = new AddColorButton (); + add_global_color_btn.clicked.connect (() => { + on_save_color (Container.GLOBAL); + }); + global_colors_flowbox.add (add_global_color_btn); + + foreach (string color in settings.global_colors) { + var btn = create_color_button (color); + global_colors_flowbox.add (btn); + } + + color_grid.attach (color_chooser_widget, 0, 0, 1, 1); + color_grid.attach (global_colors_label, 0, 1, 1, 1); + color_grid.attach (global_colors_flowbox, 0, 2, 1, 1); + color_grid.show_all (); + color_popover.add (color_grid); + + // Set the chooser color before connecting the signal. + set_chooser_color (model.color, model.alpha); + + color_chooser_widget.notify["rgba"].connect (on_color_changed); + } + + private int sort_colors_function (Gtk.FlowBoxChild a, Gtk.FlowBoxChild b) { + return (a is AddColorButton) ? -1 : 1; + } + + /* + * Add the current color to the parent flowbox. + */ + private void on_save_color (Container parent) { + // Store the currently active color. + var color = color_chooser_widget.rgba.to_string (); + + // Create the new color button and connect to its signal. + var btn = create_color_button (color); + + // Update the colors list and the schema based on the colors container. + switch (parent) { + case Container.GLOBAL: + global_colors_flowbox.add (btn); + var array = settings.global_colors; + array += color; + settings.global_colors = array; + break; + + case Container.DOCUMENT: + // TODO... + break; + } + } + + private void set_chooser_color (string color, int alpha) { + var new_rgba = Gdk.RGBA (); + new_rgba.parse (color); + new_rgba.alpha = (double) alpha / 255; + color_chooser_widget.set_rgba (new_rgba); + } + + private void set_button_color (string color, int alpha) { + try { + var provider = new Gtk.CssProvider (); + var context = color_button.get_style_context (); + + var new_rgba = Gdk.RGBA (); + new_rgba.parse (color); + new_rgba.alpha = (double) alpha / 255; + var new_color = new_rgba.to_string (); + + var css = """.selected-color { + background-color: %s; + border-color: shade (%s, 0.75); + }""".printf (new_color, new_color); + + provider.load_from_data (css, css.length); + + context.add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + } catch (Error e) { + warning ("Style error: %s", e.message); + } + } + + private Gtk.FlowBoxChild create_color_button (string color) { + var child = new Gtk.FlowBoxChild (); + child.valign = child.halign = Gtk.Align.CENTER; + + var btn = new Widgets.RoundedColorButton (color); + btn.set_color.connect ((color) => { + var rgba_color = Gdk.RGBA (); + rgba_color.parse (color); + color_chooser_widget.set_rgba (rgba_color); + }); + + child.add (btn); + child.show_all (); + return child; + } + + private void on_color_changed () { + // The color change came from the input field, prevent an infinite loop. + if (color_set_manually) { + return; + } + + // Prevent visible updated fields like hex and opacity from triggering + // the update of the model values. + color_set_manually = true; + + // Update the model values. + model.color = color_chooser_widget.rgba.to_string (); + model.alpha = (int) (color_chooser_widget.rgba.alpha * 255); + + // Update the UI. + set_button_color (model.color, model.alpha); + field.text = Utils.Color.rgba_to_hex (model.color); + if (model.type == Models.ColorModel.Type.FILL) { + opacity_field.entry.value = Math.round ((double) model.alpha / 255 * 100); + } + + // Allow manual edit from the input fields. + color_set_manually = false; + } + + private void on_eyedropper_click () { + var eyedropper = new Akira.Utils.ColorPicker (); + eyedropper.show_all (); + + eyedropper.picked.connect ((picked_color) => { + init_color_chooser (); + color_chooser_widget.set_rgba (picked_color); + eyedropper.close (); + }); + + eyedropper.cancelled.connect (() => { + eyedropper.close (); + }); + } +} diff --git a/src/meson.build b/src/meson.build index e52fb30e5..402bd3b23 100644 --- a/src/meson.build +++ b/src/meson.build @@ -59,6 +59,7 @@ sources = files( 'Widgets/AlignBoxButton.vala', 'Widgets/ButtonImage.vala', 'Widgets/ColorField.vala', + 'Widgets/ColorRow.vala', 'Widgets/ExportWidget.vala', 'Widgets/HeaderBarButton.vala', 'Widgets/InputField.vala', @@ -68,11 +69,9 @@ sources = files( 'Widgets/RoundedColorButton.vala', 'Widgets/ZoomButton.vala', - 'Models/BordersItemModel.vala', - 'Models/FillsItemModel.vala', - 'Models/BaseModel.vala', - 'Models/ListModel.vala', + 'Models/ColorModel.vala', 'Models/ExportModel.vala', + 'Models/ListModel.vala', 'Dialogs/ShortcutsDialog.vala', 'Dialogs/SettingsDialog.vala', From 5401218e39f99ff23428827971e41baff9927aca Mon Sep 17 00:00:00 2001 From: Alessandro Date: Sat, 26 Jun 2021 17:10:21 -0700 Subject: [PATCH 05/14] Fix nobs regression (#608) * Fix regression of corner nobs not shifting when the item is small * Do early return for rotation nob before other conditions * Account for item rotation. Fixes #607 --- src/Lib/Managers/NobManager.vala | 38 ++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/Lib/Managers/NobManager.vala b/src/Lib/Managers/NobManager.vala index ce8be6e71..2f7e34745 100644 --- a/src/Lib/Managers/NobManager.vala +++ b/src/Lib/Managers/NobManager.vala @@ -442,13 +442,10 @@ public class Akira.Lib.Managers.NobManager : Object { var nob_name = nob.handle_id; - calculate_nob_position ( nob_name, nob_data, ref center_x, ref center_y ); + calculate_nob_position (nob_name, nob_data, ref center_x, ref center_y); - if (!print_middle_height_nobs && (nob_name == Nob.RIGHT_CENTER || nob_name == Nob.LEFT_CENTER)) { - set_visible = false; - } else if (!print_middle_width_nobs && (nob_name == Nob.TOP_CENTER || nob_name == Nob.BOTTOM_CENTER)) { - set_visible = false; - } else if (nob.handle_id == Nob.ROTATE) { + // Unique calculation for the rotation nob. + if (nob.handle_id == Nob.ROTATE) { double line_offset_x = 0; double line_offset_y = - (LINE_HEIGHT / canvas.current_scale); nob_data.bb_matrix.transform_distance (ref line_offset_x, ref line_offset_y); @@ -480,11 +477,38 @@ public class Akira.Lib.Managers.NobManager : Object { // Raise to the rotation_line, so the line is under the rotation nob. nob.update_state (nob_data.bb_matrix, center_x, center_y, set_visible); nob.raise (rotation_line); - return; + continue; + } + + // Check if we need to hide the vertically centered nobs. + if (!print_middle_height_nobs && (nob_name == Nob.RIGHT_CENTER || nob_name == Nob.LEFT_CENTER)) { + set_visible = false; + } + + // Check if we need to hide the horizontally centere nobs. + if (!print_middle_width_nobs && (nob_name == Nob.TOP_CENTER || nob_name == Nob.BOTTOM_CENTER)) { + set_visible = false; } nob.update_state (nob_data.bb_matrix, center_x, center_y, set_visible); nob.raise (select_effect); + + // If we're hiding all centered nobs, we need to shift the position + // of the corner nobs to improve the grabbing area. + if (!print_middle_width_nobs && !print_middle_height_nobs) { + var half = nob_size / 2; + + // Use Cairo.translate to automatically account for the item's rotation. + if (nob_name == Nob.TOP_LEFT) { + nob.translate (-half, -half); + } else if (nob_name == Nob.TOP_RIGHT) { + nob.translate (half, -half); + } else if (nob_name == Nob.BOTTOM_RIGHT) { + nob.translate (half, half); + } else if (nob_name == Nob.BOTTOM_LEFT) { + nob.translate (-half, half); + } + } } } From e339e45d701f34ec5878e8bd3fcdd475a7ba66f0 Mon Sep 17 00:00:00 2001 From: Saverio Morelli <32335378+Sav22999@users.noreply.github.com> Date: Mon, 28 Jun 2021 20:18:06 +0200 Subject: [PATCH 06/14] Translated in Italian (#618) * Translated in Italian * Fixed some typos --- po/it.po | 668 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 667 insertions(+), 1 deletion(-) diff --git a/po/it.po b/po/it.po index ca301d2fd..336429033 100644 --- a/po/it.po +++ b/po/it.po @@ -1,5 +1,671 @@ +# +# XP312 < >, 2020. +# Saverio , 2021. +# msgid "" msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"Project-Id-Version: unnamed project\n" +"Last-Translator: Saverio \n" +"Language-Team: Italian \n" +"Language: it\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2021-06-28 18:59+0200\n" +"X-Generator: Gtranslator 40.0\n" + +#: Dialogs/ExportDialog.vala:151 +msgid "Export to:" +msgstr "Esporta come:" + +#: Dialogs/ExportDialog.vala:157 +msgid "Select Folder" +msgstr "Seleziona la cartella" + +#: Dialogs/ExportDialog.vala:166 +msgid "Format:" +msgstr "Formato:" + +#: Dialogs/ExportDialog.vala:180 +msgid "Quality:" +msgstr "Qualità:" + +#: Dialogs/ExportDialog.vala:192 +msgid "Compression:" +msgstr "Compressione:" + +#: Dialogs/ExportDialog.vala:206 +msgid "Transparency:" +msgstr "Trasparenza:" + +#: Dialogs/ExportDialog.vala:219 +msgid "Scale:" +msgstr "Scala:" + +#: Dialogs/ExportDialog.vala:243 FileFormat/FileManager.vala:49 +#: FileFormat/FileManager.vala:105 +msgid "Cancel" +msgstr "Annulla" + +#: Dialogs/ExportDialog.vala:250 Dialogs/ShortcutsDialog.vala:63 +#: Layouts/HeaderBar.vala:112 +msgid "Export" +msgstr "Esporta" + +#: Dialogs/ReleaseDialog.vala:47 Dialogs/SettingsDialog.vala:217 +msgid "" +"WARNING!\n" +"Akira is still under development and not ready for production. Missing " +"features, random bugs, and black holes opening in your kitchen are to be " +"expected." +msgstr "" +"ATTENZIONE!\n" +"Akira è ancora in fase di sviluppo e non è pronta per la produzione. " +"Potrebbero verificarsi bug casuali, funzionalità mancanti, o apertura di " +"buchi neri nella tua cucina." + +#: Dialogs/ReleaseDialog.vala:100 Dialogs/SettingsDialog.vala:249 +msgid "Make a Donation" +msgstr "Fai una donazione" + +#: Dialogs/ReleaseDialog.vala:109 Dialogs/SettingsDialog.vala:258 +msgid "Suggest Translations" +msgstr "Suggerisci traduzione" + +#: Dialogs/ReleaseDialog.vala:118 Dialogs/SettingsDialog.vala:267 +msgid "Report a Problem" +msgstr "Segnala un problema" + +#: Dialogs/SettingsDialog.vala:40 +msgid "Preferences" +msgstr "Preferenze" + +#: Dialogs/SettingsDialog.vala:50 Dialogs/SettingsDialog.vala:73 +#: Dialogs/ShortcutsDialog.vala:45 +msgid "General" +msgstr "Generale" + +#: Dialogs/SettingsDialog.vala:51 Dialogs/SettingsDialog.vala:86 +msgid "Interface" +msgstr "Interfaccia" + +#: Dialogs/SettingsDialog.vala:52 Layouts/HeaderBar.vala:280 +msgid "Shapes" +msgstr "Figure" + +#: Dialogs/SettingsDialog.vala:53 +msgid "About" +msgstr "Informazioni" + +# In Italian the literal translation would be a little weird, so I've added a "edited" at the end of the sentence +#: Dialogs/SettingsDialog.vala:74 +msgid "Auto Reopen Latest File:" +msgstr "Apri automaticamente l'ultimo file modificato" + +#: Dialogs/SettingsDialog.vala:88 +msgid "Use Dark Theme:" +msgstr "Utilizza il tema scuro:" + +#: Dialogs/SettingsDialog.vala:92 +msgid "Invert Panels Order:" +msgstr "Inverti l'ordine dei pannelli:" + +#: Dialogs/SettingsDialog.vala:96 +msgid "Restart application to apply this change." +msgstr "Riavvia l'applicazione applicare questa modifica" + +#: Dialogs/SettingsDialog.vala:100 +msgid "ToolBar Style" +msgstr "Stile della barra degli strumenti" + +#: Dialogs/SettingsDialog.vala:102 +msgid "Show Button Labels:" +msgstr "Mostra le etichette dei pulsanti:" + +#: Dialogs/SettingsDialog.vala:106 +msgid "Use Symbolic Icons:" +msgstr "Utilizza le icone simboliche:" + +#: Dialogs/SettingsDialog.vala:129 +msgid "Default Colors" +msgstr "Colori predefiniti" + +#: Dialogs/SettingsDialog.vala:131 +msgid "Define the default style used when creating a new shape." +msgstr "" +"Definisci lo stile predefinito usato quando viene creata una nuova figura." + +#: Dialogs/SettingsDialog.vala:136 +msgid "Fill Color:" +msgstr "Colore di riempimento:" + +#: Dialogs/SettingsDialog.vala:157 +msgid "Enable Border Style:" +msgstr "Attiva lo stile dei bordi:" + +#: Dialogs/SettingsDialog.vala:160 +msgid "Border Color:" +msgstr "Colore dei bordi:" + +#: Dialogs/SettingsDialog.vala:181 +msgid "Border Width:" +msgstr "Spessore dei bordi:" + +#: Dialogs/SettingsDialog.vala:209 +msgid "The Linux Design Tool" +msgstr "Lo strumento di design di Linux" + +#: Dialogs/SettingsDialog.vala:226 +msgid "Thanks to our awesome supporters!" +msgstr "Grazie ai nostri meravigliosi collaboratori!" + +#: Dialogs/SettingsDialog.vala:231 +msgid "View the list of supporters" +msgstr "Visualizza la lista dei collaboratori" + +#: Dialogs/ShortcutsDialog.vala:46 +msgid "New window:" +msgstr "Nuova finestra:" + +#: Dialogs/ShortcutsDialog.vala:48 +msgid "Preferences:" +msgstr "Preferenze:" + +#: Dialogs/ShortcutsDialog.vala:50 +msgid "Quit:" +msgstr "Esci:" + +#: Dialogs/ShortcutsDialog.vala:52 +msgid "Presentation mode:" +msgstr "Modalità presentazione:" + +#: Dialogs/ShortcutsDialog.vala:55 +msgid "File" +msgstr "File" + +#: Dialogs/ShortcutsDialog.vala:56 +msgid "Open:" +msgstr "Apri:" + +#: Dialogs/ShortcutsDialog.vala:58 +msgid "Save:" +msgstr "Salva:" + +#: Dialogs/ShortcutsDialog.vala:60 +msgid "Save as:" +msgstr "Salva come:" + +#: Dialogs/ShortcutsDialog.vala:64 +msgid "Export artboards:" +msgstr "Esporta artboard:" + +#: Dialogs/ShortcutsDialog.vala:66 +msgid "Export selection:" +msgstr "Esporta la selezione:" + +#: Dialogs/ShortcutsDialog.vala:68 +msgid "Highlight area to export:" +msgstr "Evidenzia l'area da esportare:" + +#: Dialogs/ShortcutsDialog.vala:77 +msgid "Canvas" +msgstr "Canvas" + +#: Dialogs/ShortcutsDialog.vala:78 +msgid "Zoom in:" +msgstr "Aumenta lo zoom:" + +#: Dialogs/ShortcutsDialog.vala:80 +msgid "Zoom out:" +msgstr "Riduci lo zoom:" + +#: Dialogs/ShortcutsDialog.vala:82 +msgid "Zoom reset:" +msgstr "Reimposta lo zoom:" + +#: Dialogs/ShortcutsDialog.vala:85 +msgid "Item creation" +msgstr "Creazione elemento" + +#: Dialogs/ShortcutsDialog.vala:86 +msgid "Artboard:" +msgstr "Artboard:" + +#: Dialogs/ShortcutsDialog.vala:88 +msgid "Rectangle:" +msgstr "Rettangolo:" + +#: Dialogs/ShortcutsDialog.vala:90 +msgid "Ellipse:" +msgstr "Ellisse:" + +#: Dialogs/ShortcutsDialog.vala:92 +msgid "Text:" +msgstr "Testo:" + +#: Dialogs/ShortcutsDialog.vala:94 +msgid "Image:" +msgstr "Immagine:" + +#: Dialogs/ShortcutsDialog.vala:97 Layouts/Partials/TransformPanel.vala:174 +msgid "Transform" +msgstr "Trasforma:" + +#: Dialogs/ShortcutsDialog.vala:98 +msgid "Raise selection:" +msgstr "Porta la selezione sopra:" + +#: Dialogs/ShortcutsDialog.vala:100 +msgid "Lower selection:" +msgstr "Porta la selezione in basso:" + +#: Dialogs/ShortcutsDialog.vala:102 +msgid "Raise selection to top:" +msgstr "Porta la selezione in primo piano:" + +#: Dialogs/ShortcutsDialog.vala:104 +msgid "Lower selection to bottom:" +msgstr "Porta la selezione in ultimo piano:" + +#: Dialogs/ShortcutsDialog.vala:106 +msgid "Flip horizontally:" +msgstr "Capovolgi orizzontalmente:" + +#: Dialogs/ShortcutsDialog.vala:108 +msgid "Flip vertically:" +msgstr "Capovolgi verticalmente:" + +#: FileFormat/FileManager.vala:46 +msgid "Save Akira file" +msgstr "Salva file Akira" + +#: FileFormat/FileManager.vala:48 Layouts/HeaderBar.vala:190 +msgid "Save" +msgstr "Salva" + +#: FileFormat/FileManager.vala:71 +msgid "Akira files" +msgstr "File Akira" + +#: FileFormat/FileManager.vala:76 +msgid "All files" +msgstr "Tutti i file" + +#: FileFormat/FileManager.vala:103 +msgid "Open Akira file" +msgstr "Apri file Akira" + +#: FileFormat/FileManager.vala:105 Layouts/HeaderBar.vala:170 +msgid "Open" +msgstr "Apri" + +#: Layouts/HeaderBar.vala:69 Lib/Managers/ExportManager.vala:296 +msgid "Untitled" +msgstr "Senza titolo" + +#: Layouts/HeaderBar.vala:71 +msgid "Menu" +msgstr "Menu" + +#: Layouts/HeaderBar.vala:75 +msgid "Insert" +msgstr "Inserisci" + +#: Layouts/HeaderBar.vala:82 +msgid "Group" +msgstr "Ragruppa" + +#: Layouts/HeaderBar.vala:84 +msgid "Ungroup" +msgstr "Separa" + +#: Layouts/HeaderBar.vala:87 +msgid "Up" +msgstr "Sopra" + +#: Layouts/HeaderBar.vala:92 +msgid "Down" +msgstr "Sotto" + +#: Layouts/HeaderBar.vala:97 +msgid "Top" +msgstr "Più in alto" + +#: Layouts/HeaderBar.vala:102 +msgid "Bottom" +msgstr "Più in basso" + +#: Layouts/HeaderBar.vala:107 +msgid "Settings" +msgstr "Impostazioni" + +# ? +#: Layouts/HeaderBar.vala:118 +msgid "Difference" +msgstr "Differenza" + +#: Layouts/HeaderBar.vala:120 +msgid "Exclusion" +msgstr "Esclusione" + +#: Layouts/HeaderBar.vala:122 +msgid "Intersect" +msgstr "Intersezione" + +#: Layouts/HeaderBar.vala:124 +msgid "Union" +msgstr "Unione" + +#: Layouts/HeaderBar.vala:161 +msgid "New Window" +msgstr "Nuova finestra" + +#: Layouts/HeaderBar.vala:182 +msgid "Open Recent" +msgstr "Apri recenti" + +#: Layouts/HeaderBar.vala:195 +msgid "Save As" +msgstr "Salva come" + +#: Layouts/HeaderBar.vala:204 +msgid "Quit" +msgstr "Esci" + +#: Layouts/HeaderBar.vala:237 +msgid "Artboard" +msgstr "Artboard" + +#: Layouts/HeaderBar.vala:254 +msgid "Add Items" +msgstr "Aggiungi elementi" + +#: Layouts/HeaderBar.vala:262 +msgid "Rectangle" +msgstr "Rettangolo" + +#: Layouts/HeaderBar.vala:268 +msgid "Ellipse" +msgstr "Ellisse" + +#: Layouts/HeaderBar.vala:286 +msgid "Vector" +msgstr "Vettore" + +#: Layouts/HeaderBar.vala:288 +msgid "Pencil" +msgstr "Matita" + +#: Layouts/HeaderBar.vala:291 +msgid "Text" +msgstr "Testo" + +#: Layouts/HeaderBar.vala:297 +msgid "Image" +msgstr "Immagine" + +#: Layouts/HeaderBar.vala:330 +msgid "Export Current Selection" +msgstr "Esporta selezione corrente" + +#: Layouts/HeaderBar.vala:336 +msgid "Export Artboards" +msgstr "Esporta le artboard" + +#: Layouts/HeaderBar.vala:345 +msgid "Highlight Area to Export" +msgstr "Evidenza l'area da esportare" + +#: Layouts/HeaderBar.vala:391 +msgid "Main Menu" +msgstr "Menu principale" + +#: Layouts/HeaderBar.vala:468 Services/ActionManager.vala:218 +#: Services/ActionManager.vala:237 Services/ActionManager.vala:256 +#, c-format +msgid "Unable to open file at '%s'" +msgstr "Non è possibile aprirle il file “%s”" + +#: Layouts/MainCanvas.vala:52 +msgid "Button was pressed!" +msgstr "Il pulsante è stato premuto!" + +#: Layouts/MainCanvas.vala:160 +msgid "Export completed!" +msgstr "Esportazione completata!" + +#: Layouts/Partials/AlignItemsPanel.vala:48 +msgid "Distribute Horizontally" +msgstr "Distribuisci orizzontalmente" + +#: Layouts/Partials/AlignItemsPanel.vala:50 +msgid "Distribute Vertically" +msgstr "Distribuisci verticalmente" + +#: Layouts/Partials/AlignItemsPanel.vala:52 +msgid "Align Left" +msgstr "Allineamento a sinistra" + +#: Layouts/Partials/AlignItemsPanel.vala:53 +msgid "Align Center" +msgstr "Allineamento al centro" + +#: Layouts/Partials/AlignItemsPanel.vala:54 +msgid "Align Right" +msgstr "Allicenamento a destra" + +#: Layouts/Partials/AlignItemsPanel.vala:56 +msgid "Align Top" +msgstr "Allineamento in alto" + +#: Layouts/Partials/AlignItemsPanel.vala:57 +msgid "Align Middle" +msgstr "Allineamento al centro" + +#: Layouts/Partials/AlignItemsPanel.vala:58 +msgid "Align Bottom" +msgstr "Allineamento in basso" + +#: Layouts/Partials/Artboard.vala:144 Layouts/Partials/Artboard.vala:487 +#: Layouts/Partials/Layer.vala:139 Layouts/Partials/Layer.vala:687 +msgid "Lock Layer" +msgstr "Blocca livello" + +#: Layouts/Partials/Artboard.vala:155 Layouts/Partials/Artboard.vala:529 +#: Layouts/Partials/Layer.vala:156 Layouts/Partials/Layer.vala:729 +msgid "Hide Layer" +msgstr "Nascondi livello" + +#: Layouts/Partials/Artboard.vala:487 Layouts/Partials/Layer.vala:687 +msgid "Unlock Layer" +msgstr "Sblocca livello" + +#: Layouts/Partials/Artboard.vala:529 Layouts/Partials/Layer.vala:729 +msgid "Show Layer" +msgstr "Mostra livello" + +#: Layouts/Partials/BorderRadiusPanel.vala:86 +msgid "Style" +msgstr "Stile" + +#: Layouts/Partials/BorderRadiusPanel.vala:103 +msgid "Border Radius" +msgstr "Raggio bordo" + +#: Layouts/Partials/BorderRadiusPanel.vala:198 +msgid "Autoscale Corners" +msgstr "Angoli automaticamente scalati" + +#: Layouts/Partials/BorderRadiusPanel.vala:207 +msgid "Uniform Corners" +msgstr "Angoli uniformati" + +#: Layouts/Partials/BordersPanel.vala:53 +msgid "Borders" +msgstr "Bordi" + +#: Layouts/Partials/FillsPanel.vala:54 +msgid "Fills" +msgstr "Riempi" + +#: Layouts/Partials/TransformPanel.vala:96 +msgid "X" +msgstr "X" + +#: Layouts/Partials/TransformPanel.vala:96 +msgid "Horizontal position" +msgstr "Posizione orizzontale" + +#: Layouts/Partials/TransformPanel.vala:98 +msgid "Y" +msgstr "Y" + +#: Layouts/Partials/TransformPanel.vala:98 +msgid "Vertical position" +msgstr "Posizione verticale" + +#: Layouts/Partials/TransformPanel.vala:100 +msgid "W" +msgstr "W" + +#: Layouts/Partials/TransformPanel.vala:100 +msgid "Width" +msgstr "Larghezza" + +#: Layouts/Partials/TransformPanel.vala:102 +msgid "H" +msgstr "H" + +#: Layouts/Partials/TransformPanel.vala:102 +msgid "Height" +msgstr "Altezza" + +#: Layouts/Partials/TransformPanel.vala:107 +msgid "Lock Ratio" +msgstr "Blocca proporzioni" + +#: Layouts/Partials/TransformPanel.vala:114 +msgid "R" +msgstr "R" + +#: Layouts/Partials/TransformPanel.vala:114 +msgid "Rotation degrees" +msgstr "Angolo di rotazione" + +#: Layouts/Partials/TransformPanel.vala:126 +msgid "Flip Horizontally" +msgstr "Capovolgi orizzontalmente" + +#: Layouts/Partials/TransformPanel.vala:137 +msgid "Flip Vertically" +msgstr "Capovolgi verticalmente" + +#: Layouts/Partials/TransformPanel.vala:165 +msgid "Position" +msgstr "Posizione" + +#: Layouts/Partials/TransformPanel.vala:169 +msgid "Size" +msgstr "Grandezza" + +#: Layouts/Partials/TransformPanel.vala:178 +msgid "Opacity" +msgstr "Opacità" + +#: Layouts/RightSideBar.vala:111 +msgid "Search Layer" +msgstr "Cerca livello" + +#: Lib/Managers/ExportManager.vala:192 Lib/Managers/ExportManager.vala:219 +msgid "Generating preview, please wait…" +msgstr "Generazione dell'anteprima in corso…" + +#: Lib/Managers/ExportManager.vala:313 +#, c-format +msgid "Untitled %i" +msgstr "Senza titolo %i" + +#: Lib/Managers/ExportManager.vala:484 +msgid "Exporting images…" +msgstr "Esportazione delle immagini in corso…" + +#: Partials/ExportWidget.vala:117 +#, c-format +msgid "%i × %i px · %s" +msgstr "%i × %i px · %s" + +#: Partials/ExportWidget.vala:124 +msgid "Fetching image size…" +msgstr "Calcolo delle dimensioni dell'immagine in corso…" + +#: Partials/ZoomButton.vala:49 +msgid "Zoom Out" +msgstr "Riduci zoom" + +#: Partials/ZoomButton.vala:55 +msgid "Reset Zoom" +msgstr "Reimposta zoom" + +#: Partials/ZoomButton.vala:62 +msgid "Zoom In" +msgstr "Aumenta zoom" + +#: Partials/ZoomButton.vala:68 +msgid "Zoom" +msgstr "Zoom" + +#: Services/ActionManager.vala:211 +msgid "No recently opened file available!" +msgstr "Nessun file aperto recentemente disponibile!" + +#: Services/ActionManager.vala:230 +msgid "No second most recently opened file available!" +msgstr "Nessun secondo file aperto recentemente disponibile!" + +#: Services/ActionManager.vala:249 +msgid "No third most recently opened file available!" +msgstr "Nessun terzo file aperto recentemente disponibile!" + +#: Services/ActionManager.vala:287 +msgid "Nothing selected to export!" +msgstr "Nessuna selezione da esportare!" + +#: Services/ActionManager.vala:296 +msgid "Export of Artboards currently unavailable…sorry 😑️" +msgstr "" +"Spiacente, l'esportazione delle artboard non è al momento disponibile 😑️" + +#: Services/ActionManager.vala:363 +msgid "Choose image file" +msgstr "Scegli l'immagine" + +#: Services/ActionManager.vala:363 +msgid "Select" +msgstr "Seleziona" + +#: Services/ActionManager.vala:363 +msgid "Close" +msgstr "Chiudi" + +#: Services/ActionManager.vala:432 +#, c-format +msgid "Error! .%s files are not supported!" +msgstr "Errore! I file %s non sono supportati." + +#: Window.vala:119 +msgid "Are you sure you want to quit?" +msgstr "Sei sicuro di voler uscire?" + +#: Window.vala:120 +msgid "All unsaved data will be lost and impossible to recover." +msgstr "" +"Tutti i dati non salvati saranno persi e non sarà possibile recuperarli." + +#: Window.vala:122 +msgid "Quit without saving!" +msgstr "Esci senza salvare!" + +#: Window.vala:123 +msgid "Save file" +msgstr "Salva file" From 51268af2e3dc7a06b5358991fa29494a1d0bf4df Mon Sep 17 00:00:00 2001 From: Mkefss Date: Mon, 28 Jun 2021 14:53:00 -0500 Subject: [PATCH 07/14] Color picker working under wayland --- .../flatpak/com.github.akiraux.akira.json | 11 +- meson.build | 1 + src/Layouts/Partials/FillItem.vala | 7 + src/Services/ActionManager.vala | 15 - src/Utils/ColorPicker.vala | 349 ++---------------- src/Widgets/ColorRow.vala | 6 - src/meson.build | 1 + vapi/libportal.vapi | 29 ++ 8 files changed, 79 insertions(+), 340 deletions(-) create mode 100644 vapi/libportal.vapi diff --git a/build-aux/flatpak/com.github.akiraux.akira.json b/build-aux/flatpak/com.github.akiraux.akira.json index 854b41887..372ec3dd3 100644 --- a/build-aux/flatpak/com.github.akiraux.akira.json +++ b/build-aux/flatpak/com.github.akiraux.akira.json @@ -23,7 +23,16 @@ "--filesystem=xdg-run/gvfs", "--filesystem=xdg-run/gvfsd" ], - "modules": [{ + "modules": [ + { + "name": "libportal", + "buildsystem": "meson", + "sources": [{ + "type": "git", + "url": "https://github.com/flatpak/libportal.git" + }] + }, + { "name": "goocanvas", "config-opts": ["--enable-python=no"], "build-options": { diff --git a/meson.build b/meson.build index 5185ccfa8..ea774fae1 100644 --- a/meson.build +++ b/meson.build @@ -22,6 +22,7 @@ cairo_dependency = dependency('cairo', version: '>=1.14') goocanvas_dependency = dependency('goocanvas-3.0') libarchive_dependency = dependency('libarchive') json_glib_dependency = dependency('json-glib-1.0') +dep_libportal = dependency('libportal', required: true) # Optional dependencies desktop_file_validate = find_program('desktop-file-validate', required: false) diff --git a/src/Layouts/Partials/FillItem.vala b/src/Layouts/Partials/FillItem.vala index f7bf349ce..e05baa633 100644 --- a/src/Layouts/Partials/FillItem.vala +++ b/src/Layouts/Partials/FillItem.vala @@ -86,6 +86,13 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { hidden_button.clicked.connect (toggle_visibility); } + private void on_color_changed () { + color_set_manually = true; + color = color_chooser_widget.rgba.to_string (); + alpha = ((int)(color_chooser_widget.rgba.alpha * 255)); + set_button_color (); + } + private void on_delete_item () { fill.remove (); fill_deleted (); diff --git a/src/Services/ActionManager.vala b/src/Services/ActionManager.vala index 61466963b..552ab2ddb 100644 --- a/src/Services/ActionManager.vala +++ b/src/Services/ActionManager.vala @@ -470,19 +470,6 @@ public class Akira.Services.ActionManager : Object { bool is_holding_shift = false; var color_picker = new Akira.Utils.ColorPicker (); - color_picker.show_all (); - - color_picker.key_pressed.connect (e => { - is_holding_shift = e.keyval == Gdk.Key.Shift_L; - }); - - color_picker.key_released.connect (e => { - is_holding_shift = e.keyval == Gdk.Key.Shift_L; - }); - - color_picker.cancelled.connect (() => { - color_picker.close (); - }); color_picker.picked.connect (color => { foreach (var item in canvas.selected_bound_manager.selected_items) { @@ -500,8 +487,6 @@ public class Akira.Services.ActionManager : Object { item.fills.update_color_from_action (color); } - color_picker.close (); - // Force a UI reload of the fills and borders panel since some items // had their properties changed. canvas.window.event_bus.selected_items_list_changed (canvas.selected_bound_manager.selected_items); diff --git a/src/Utils/ColorPicker.vala b/src/Utils/ColorPicker.vala index 249377052..467d3b170 100644 --- a/src/Utils/ColorPicker.vala +++ b/src/Utils/ColorPicker.vala @@ -20,323 +20,36 @@ * Ported from: https://github.com/ColorPicker/RonnyDo */ -public class Akira.Utils.ColorPicker : Gtk.Window { - public signal void picked (Gdk.RGBA color); - public signal void cancelled (); - public signal void moved (Gdk.RGBA color); - public signal void key_pressed (Gdk.EventKey e); - public signal void key_released (Gdk.EventKey e); +public class Akira.Utils.ColorPicker { + public signal void picked (Gdk.RGBA color); + private Xdp.Parent parent; + private Xdp.Portal portal; + + + public ColorPicker () { + Gtk.Application app = (Gtk.Application) GLib.Application.get_default (); + Gtk.Window window = app.get_active_window (); + + parent = new Xdp.Parent (window); + portal = new Xdp.Portal (); + + portal.pick_color.begin(parent, null, picked_color); + } + + public void picked_color (GLib.Object? object, GLib.AsyncResult? result) { + GLib.Variant v = portal.pick_color_finish (result); + if (v == null) + return; + + Gdk.RGBA color = Gdk.RGBA (); + color.alpha = 1; + + VariantIter iterator = v.iterator (); + iterator.next ("d", &color.red); + iterator.next ("d", &color.green); + iterator.next ("d", &color.blue); + + picked (color); + } - const string DARK_BORDER_COLOR_STRING = "#333333"; - private Gdk.RGBA dark_border_color = Gdk.RGBA (); - - const string BRIGHT_BORDER_COLOR_STRING = "#FFFFFF"; - private Gdk.RGBA bright_border_color = Gdk.RGBA (); - - - // 1. Snapsize is the amount of pixel going to be magnified by the zoomlevel. - // 2. The snapsize must be odd to have a 1px magnifier center. - // 3. Asure that snapsize*max_zoomlevel+shadow_width*2 is smaller than 2 * get_screen ().get_display ().get_maximal_cursor_size() - // Valid: snapsize = 31, max_zoomlevel = 7, shadow_width = 15 --> 247px - // get_maximal_cursor_size = 128 --> 256px - // Otherwise the cursor starts to flicker. See https://github.com/stuartlangridge/ColourPicker/issues/6#issuecomment-277972290 - // and https://github.com/RonnyDo/ColorPicker/issues/19 - int snapsize = 31; - int min_zoomlevel = 2; - int max_zoomlevel = 7; - int zoomlevel = 6; - int shadow_width = 15; - - private Gdk.Cursor magnifier = null; - - construct { - app_paintable = true; - decorated = false; - resizable = false; - set_visual (get_screen ().get_rgba_visual ()); - type = Gtk.WindowType.POPUP; - } - - - public ColorPicker () { - stick (); - set_resizable (true); - set_deletable (false); - set_skip_taskbar_hint (true); - set_skip_pager_hint (true); - set_keep_above (true); - - - dark_border_color.parse (DARK_BORDER_COLOR_STRING); - bright_border_color.parse (BRIGHT_BORDER_COLOR_STRING); - - // TODO remove the zoom level restauration if we do not need it - // restore zoomlevel - // if (settings.zoomlevel >= min_zoomlevel && settings.zoomlevel <= max_zoomlevel) { - // zoomlevel = settings.zoomlevel; - // } - - var display = Gdk.Display.get_default (); - Gdk.Monitor monitor = display.get_primary_monitor (); - Gdk.Rectangle geom = monitor.get_geometry (); - set_default_size (geom.width, geom.height); - } - - - public override bool button_release_event (Gdk.EventButton e) { - // button_1 is left mouse button - if (e.button == 1) { - Gdk.RGBA color = get_color_at ((int) e.x_root, (int) e.y_root); - picked (color); - // button_3 is right mouse button - } else if (e.button == 3) { - cancelled (); - } - - return true; - } - - - public override bool draw (Cairo.Context cr) { - return false; - } - - - public override bool motion_notify_event (Gdk.EventMotion e) { - Gdk.RGBA color = get_color_at ((int) e.x_root, (int) e.y_root); - - moved (color); - - set_magnifier_cursor (); - - return true; - } - - - public override bool scroll_event (Gdk.EventScroll e) { - switch (e.direction) { - case Gdk.ScrollDirection.UP: - if (zoomlevel < max_zoomlevel) { - zoomlevel++; - } - set_magnifier_cursor (); - break; - case Gdk.ScrollDirection.DOWN: - if (zoomlevel > min_zoomlevel) { - zoomlevel--; - } - set_magnifier_cursor (); - break; - default: - break; - } - - return true; - } - - public void set_magnifier_cursor () { - var manager = Gdk.Display.get_default ().get_default_seat (); - - // get cursor position - int px, py; - get_window ().get_device_position (manager.get_pointer (), out px, out py, null); - - var radius = snapsize * zoomlevel / 2; - - // get a small area (snap) meant to be zoomed - var snapped_pixbuf = snap (px - snapsize / 2, py - snapsize / 2, snapsize, snapsize); - - // Zoom that screenshot up, and grab a snapsize-sized piece from the middle - var scaled_pb = snapped_pixbuf.scale_simple ( - snapsize * zoomlevel + shadow_width * 2 , - snapsize * zoomlevel + shadow_width * 2 , - Gdk.InterpType.NEAREST - ); - - - // Create the base surface for our cursor - var base_surface = new Cairo.ImageSurface ( - Cairo.Format.ARGB32, - snapsize * zoomlevel + shadow_width * 2 , - snapsize * zoomlevel + shadow_width * 2 - ); - - var base_context = new Cairo.Context (base_surface); - - - // Create the circular path on our base surface - base_context.arc (radius + shadow_width, radius + shadow_width, radius, 0, 2 * Math.PI); - - // Paste in the screenshot - Gdk.cairo_set_source_pixbuf (base_context, scaled_pb, 0, 0); - - // Clip to that circular path, keeping the path around for later, and paint the pasted screenshot - base_context.save (); - base_context.clip_preserve (); - base_context.paint (); - base_context.restore (); - - - // Draw a shadow as outside magnifier border - double shadow_alpha = 0.6; - base_context.set_line_width (1); - - for (int i = 0; i <= shadow_width; i++) { - base_context.arc ( - radius + shadow_width, radius + shadow_width, - radius + shadow_width - i, 0, 2 * Math.PI - ); - Gdk.RGBA shadow_color = Gdk.RGBA (); - shadow_color.parse (DARK_BORDER_COLOR_STRING); - shadow_color.alpha = shadow_alpha / ((shadow_width - i + 1) * (shadow_width - i + 1)); - Gdk.cairo_set_source_rgba (base_context, shadow_color); - base_context.stroke (); - } - - - // Draw an outside bright magnifier border - Gdk.cairo_set_source_rgba (base_context, bright_border_color); - base_context.arc (radius + shadow_width, radius + shadow_width, radius - 1, 0, 2 * Math.PI); - base_context.stroke (); - - - // Draw inside square - base_context.set_line_width (1); - - Gdk.cairo_set_source_rgba (base_context, dark_border_color); - base_context.move_to (radius + shadow_width - zoomlevel, radius + shadow_width - zoomlevel); - base_context.line_to (radius + shadow_width + zoomlevel, radius + shadow_width - zoomlevel); - base_context.line_to (radius + shadow_width + zoomlevel, radius + shadow_width + zoomlevel); - base_context.line_to (radius + shadow_width - zoomlevel, radius + shadow_width + zoomlevel); - base_context.close_path (); - base_context.stroke (); - - Gdk.cairo_set_source_rgba (base_context, bright_border_color); - base_context.move_to (radius + shadow_width - zoomlevel + 1, radius + shadow_width - zoomlevel + 1); - base_context.line_to (radius + shadow_width + zoomlevel - 1, radius + shadow_width - zoomlevel + 1); - base_context.line_to (radius + shadow_width + zoomlevel - 1, radius + shadow_width + zoomlevel - 1); - base_context.line_to (radius + shadow_width - zoomlevel + 1, radius + shadow_width + zoomlevel - 1); - base_context.close_path (); - base_context.stroke (); - - - magnifier = new Gdk.Cursor.from_surface ( - get_screen ().get_display (), - base_surface, - base_surface.get_width () / 2, - base_surface.get_height () / 2); - - // Set the cursor - manager.grab ( - get_window (), - Gdk.SeatCapabilities.ALL, - true, - magnifier, - new Gdk.Event (Gdk.EventType.BUTTON_PRESS | Gdk.EventType.MOTION_NOTIFY | Gdk.EventType.SCROLL), - null); - - } - - - public Gdk.Pixbuf? snap (int x, int y, int w, int h) { - var root = Gdk.get_default_root_window (); - - var screenshot = Gdk.pixbuf_get_from_window (root, x, y, w, h); - return screenshot; - } - - - public override bool key_press_event (Gdk.EventKey e) { - var manager = Gdk.Display.get_default ().get_default_seat (); - int px, py; - get_window ().get_device_position (manager.get_pointer (), out px, out py, null); - - switch (e.keyval) { - case Gdk.Key.Escape: - cancelled (); - break; - case Gdk.Key.Return: - Gdk.RGBA color = get_color_at (px, py); - picked (color); - break; - case Gdk.Key.Up: - manager.get_pointer ().warp (get_screen (), px, py - 1); - break; - case Gdk.Key.Down: - manager.get_pointer ().warp (get_screen (), px, py + 1); - break; - case Gdk.Key.Left: - manager.get_pointer ().warp (get_screen (), px - 1, py); - break; - case Gdk.Key.Right: - manager.get_pointer ().warp (get_screen (), px + 1, py); - break; - } - - key_pressed (e); - - return true; - } - - public override bool key_release_event (Gdk.EventKey e) { - key_released (e); - - return true; - } - - public Gdk.RGBA get_color_at (int x, int y) { - var root = Gdk.get_default_root_window (); - Gdk.Pixbuf? pixbuf = Gdk.pixbuf_get_from_window (root, x, y, 1, 1); - - if (pixbuf != null) { - // see https://hackage.haskell.org/package/gtk3-0.14.6/docs/Graphics-UI-Gtk-Gdk-Pixbuf.html - uint8 red = pixbuf.get_pixels ()[0]; - uint8 green = pixbuf.get_pixels ()[1]; - uint8 blue = pixbuf.get_pixels ()[2]; - - Gdk.RGBA color = Gdk.RGBA (); - string spec = "rgb(" + red.to_string () + "," + green.to_string () + "," + blue.to_string () + ")"; - if (color.parse (spec)) { - return color; - } else { - stdout.printf ("ERROR: Parse pixel rgb values failed."); - } - } - - // fallback: default RGBA color - stdout.printf ("ERROR: Gdk.pixbuf_get_from_window failed"); - return Gdk.RGBA (); - } - - - public override void show_all () { - base.show_all (); - - var manager = Gdk.Display.get_default ().get_default_seat (); - var window = get_window (); - - var status = manager.grab ( - window, - Gdk.SeatCapabilities.ALL, - false, - new Gdk.Cursor.for_display (window.get_display (), Gdk.CursorType.CROSSHAIR), - new Gdk.Event (Gdk.EventType.BUTTON_PRESS | Gdk.EventType.BUTTON_RELEASE | Gdk.EventType.MOTION_NOTIFY), - null); - - if (status != Gdk.GrabStatus.SUCCESS) { - manager.ungrab (); - } - - // show magnifier - set_magnifier_cursor (); - } - - public new void close () { - // TODO remove the zoom level saving if we do not need it - // save zoomlevel - // settings.zoomlevel = zoomlevel; - - get_window ().set_cursor (null); - base.close (); - } } diff --git a/src/Widgets/ColorRow.vala b/src/Widgets/ColorRow.vala index 2d39ac5f2..f5d457702 100644 --- a/src/Widgets/ColorRow.vala +++ b/src/Widgets/ColorRow.vala @@ -328,16 +328,10 @@ public class Akira.Widgets.ColorRow : Gtk.Grid { private void on_eyedropper_click () { var eyedropper = new Akira.Utils.ColorPicker (); - eyedropper.show_all (); eyedropper.picked.connect ((picked_color) => { init_color_chooser (); color_chooser_widget.set_rgba (picked_color); - eyedropper.close (); - }); - - eyedropper.cancelled.connect (() => { - eyedropper.close (); }); } } diff --git a/src/meson.build b/src/meson.build index 402bd3b23..16c066c39 100644 --- a/src/meson.build +++ b/src/meson.build @@ -132,6 +132,7 @@ deps = [ goocanvas_dependency, libarchive_dependency, json_glib_dependency, + dep_libportal, m_dep ] diff --git a/vapi/libportal.vapi b/vapi/libportal.vapi new file mode 100644 index 000000000..40876da45 --- /dev/null +++ b/vapi/libportal.vapi @@ -0,0 +1,29 @@ +/* + * libportal vapi + * This is only the vapi for color picking + * */ + +[CCode (cheader_filename = "libportal/portal-gtk3.h")] +namespace Xdp { + public class Portal : GLib.Object { + public Portal (); + + public async void + pick_color (Parent parent, GLib.Cancellable? cancelable = null); + + public GLib.Variant + pick_color_finish (GLib.AsyncResult result) throws GLib.Error; + } + + [Compact] + [CCode (cname = "XdpParent", free_function = "xdp_parent_free", has_type_id = false)] + public class Parent { + public Gtk.Window object; + + [CCode (cname = "xdp_parent_new_gtk")] + public Parent (Gtk.Window window); + + [CCode (cname = "_xdp_parent_export_gtk")] + public async bool export_gtk () throws GLib.Error; + } +} From 1241a96241f8dc1a5b4e06a8133c8dcc2cdd1539 Mon Sep 17 00:00:00 2001 From: Mkefs <68037616+Mkefs@users.noreply.github.com> Date: Mon, 28 Jun 2021 15:56:57 -0500 Subject: [PATCH 08/14] Update meson.build Co-authored-by: Bilal Elmoussaoui --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ea774fae1..9ff6f0e15 100644 --- a/meson.build +++ b/meson.build @@ -22,7 +22,7 @@ cairo_dependency = dependency('cairo', version: '>=1.14') goocanvas_dependency = dependency('goocanvas-3.0') libarchive_dependency = dependency('libarchive') json_glib_dependency = dependency('json-glib-1.0') -dep_libportal = dependency('libportal', required: true) +libportal_dependency = dependency('libportal') # Optional dependencies desktop_file_validate = find_program('desktop-file-validate', required: false) From 504549f542dee1461a8b3aa144017b721d0bc6c7 Mon Sep 17 00:00:00 2001 From: Mkefss Date: Mon, 28 Jun 2021 16:04:58 -0500 Subject: [PATCH 09/14] libporta_dependency --- src/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meson.build b/src/meson.build index 16c066c39..133f6b908 100644 --- a/src/meson.build +++ b/src/meson.build @@ -132,7 +132,7 @@ deps = [ goocanvas_dependency, libarchive_dependency, json_glib_dependency, - dep_libportal, + libportal_dependency, m_dep ] From af9b81b237be42403db8734c48eaa7fe42c27dca Mon Sep 17 00:00:00 2001 From: Mkefss Date: Mon, 28 Jun 2021 16:15:55 -0500 Subject: [PATCH 10/14] Using a especific libportal version --- build-aux/flatpak/com.github.akiraux.akira.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build-aux/flatpak/com.github.akiraux.akira.json b/build-aux/flatpak/com.github.akiraux.akira.json index 372ec3dd3..4e4b54409 100644 --- a/build-aux/flatpak/com.github.akiraux.akira.json +++ b/build-aux/flatpak/com.github.akiraux.akira.json @@ -29,7 +29,8 @@ "buildsystem": "meson", "sources": [{ "type": "git", - "url": "https://github.com/flatpak/libportal.git" + "url": "https://github.com/flatpak/libportal.git", + "tag": "0.4" }] }, { From 112332f56db9c211c05ff912059f27938e4c69ec Mon Sep 17 00:00:00 2001 From: Mkefss Date: Thu, 1 Jul 2021 20:41:27 -0500 Subject: [PATCH 11/14] change indent --- .github/workflows/build.yml | 2 +- src/Utils/ColorPicker.vala | 47 ++++++++++++++++++------------------- vapi/libportal.vapi | 3 --- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e593158ff..77272bacb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: - name: Install Dependencies run: | apt update - apt install -y libarchive-dev valac meson appstream-util xvfb at-spi2-core git build-essential autoconf autoconf-archive autopoint automake pkg-config libtool m4 autoconf-archive gtk-doc-tools libxml2-utils gobject-introspection libgirepository1.0-dev libglib2.0-dev libjson-glib-dev gettext libcairo2-dev libgtk-3-dev + apt install -y libarchive-dev libportal-dev valac meson appstream-util xvfb at-spi2-core git build-essential autoconf autoconf-archive autopoint automake pkg-config libtool m4 autoconf-archive gtk-doc-tools libxml2-utils gobject-introspection libgirepository1.0-dev libglib2.0-dev libjson-glib-dev gettext libcairo2-dev libgtk-3-dev - name: goocanvas-3.0 from source run: | git clone https://gitlab.gnome.org/GNOME/goocanvas diff --git a/src/Utils/ColorPicker.vala b/src/Utils/ColorPicker.vala index 467d3b170..aa79cc209 100644 --- a/src/Utils/ColorPicker.vala +++ b/src/Utils/ColorPicker.vala @@ -21,35 +21,34 @@ */ public class Akira.Utils.ColorPicker { - public signal void picked (Gdk.RGBA color); - private Xdp.Parent parent; - private Xdp.Portal portal; + public signal void picked (Gdk.RGBA color); + private Xdp.Parent parent; + private Xdp.Portal portal; - public ColorPicker () { - Gtk.Application app = (Gtk.Application) GLib.Application.get_default (); - Gtk.Window window = app.get_active_window (); - - parent = new Xdp.Parent (window); - portal = new Xdp.Portal (); + public ColorPicker () { + Gtk.Application app = (Gtk.Application) GLib.Application.get_default (); + Gtk.Window window = app.get_active_window (); - portal.pick_color.begin(parent, null, picked_color); - } + parent = new Xdp.Parent (window); + portal = new Xdp.Portal (); - public void picked_color (GLib.Object? object, GLib.AsyncResult? result) { - GLib.Variant v = portal.pick_color_finish (result); - if (v == null) - return; - - Gdk.RGBA color = Gdk.RGBA (); - color.alpha = 1; + portal.pick_color.begin(parent, null, picked_color); + } - VariantIter iterator = v.iterator (); - iterator.next ("d", &color.red); - iterator.next ("d", &color.green); - iterator.next ("d", &color.blue); + public void picked_color (GLib.Object? object, GLib.AsyncResult? result) { + GLib.Variant v = portal.pick_color_finish (result); + if (v == null) + return; - picked (color); - } + Gdk.RGBA color = Gdk.RGBA (); + color.alpha = 1; + VariantIter iterator = v.iterator (); + iterator.next ("d", &color.red); + iterator.next ("d", &color.green); + iterator.next ("d", &color.blue); + + picked (color); + } } diff --git a/vapi/libportal.vapi b/vapi/libportal.vapi index 40876da45..91c869fb5 100644 --- a/vapi/libportal.vapi +++ b/vapi/libportal.vapi @@ -22,8 +22,5 @@ namespace Xdp { [CCode (cname = "xdp_parent_new_gtk")] public Parent (Gtk.Window window); - - [CCode (cname = "_xdp_parent_export_gtk")] - public async bool export_gtk () throws GLib.Error; } } From 8bdea71c14e226e8d0c17ffa5ba319171db3efd2 Mon Sep 17 00:00:00 2001 From: Mauricio Date: Mon, 12 Jul 2021 12:07:11 -0500 Subject: [PATCH 12/14] Updated git workflow dependencies and lint fix --- .github/workflows/build.yml | 9 ++++++++- src/Utils/ColorPicker.vala | 3 +-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 77272bacb..20672197a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: - name: Install Dependencies run: | apt update - apt install -y libarchive-dev libportal-dev valac meson appstream-util xvfb at-spi2-core git build-essential autoconf autoconf-archive autopoint automake pkg-config libtool m4 autoconf-archive gtk-doc-tools libxml2-utils gobject-introspection libgirepository1.0-dev libglib2.0-dev libjson-glib-dev gettext libcairo2-dev libgtk-3-dev + apt install -y libarchive-dev valac meson appstream-util xvfb at-spi2-core git build-essential autoconf autoconf-archive autopoint automake pkg-config libtool m4 autoconf-archive gtk-doc-tools libxml2-utils gobject-introspection libgirepository1.0-dev libglib2.0-dev libjson-glib-dev gettext libcairo2-dev libgtk-3-dev - name: goocanvas-3.0 from source run: | git clone https://gitlab.gnome.org/GNOME/goocanvas @@ -29,6 +29,13 @@ jobs: ../configure --prefix /usr make make install + - name: libportal-4.0 from source + run: | + git cline https://github.com/flatpak/libportal/ + cd libportal + git checkout f68764e + meson build + ninja install - name: Build # env: # DESTDIR: out diff --git a/src/Utils/ColorPicker.vala b/src/Utils/ColorPicker.vala index aa79cc209..743e24747 100644 --- a/src/Utils/ColorPicker.vala +++ b/src/Utils/ColorPicker.vala @@ -17,7 +17,6 @@ * along with Akira. If not, see . * * Authored by: Ivan "isneezy" Vilanculo -* Ported from: https://github.com/ColorPicker/RonnyDo */ public class Akira.Utils.ColorPicker { @@ -33,7 +32,7 @@ public class Akira.Utils.ColorPicker { parent = new Xdp.Parent (window); portal = new Xdp.Portal (); - portal.pick_color.begin(parent, null, picked_color); + portal.pick_color.begin (parent, null, picked_color); } public void picked_color (GLib.Object? object, GLib.AsyncResult? result) { From 6cc35747884cc1d07f74a9127c4e2eb2e4dd38a5 Mon Sep 17 00:00:00 2001 From: Mkefs <68037616+Mkefs@users.noreply.github.com> Date: Thu, 15 Jul 2021 17:06:57 -0500 Subject: [PATCH 13/14] Update .github/workflows/build.yml Co-authored-by: Bilal Elmoussaoui --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 20672197a..ec5b1e5b4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: make install - name: libportal-4.0 from source run: | - git cline https://github.com/flatpak/libportal/ + git clone https://github.com/flatpak/libportal.git cd libportal git checkout f68764e meson build From 7e9fbe0c20f315e049d709554792946f71060b86 Mon Sep 17 00:00:00 2001 From: Mauricio Date: Thu, 15 Jul 2021 20:23:04 -0500 Subject: [PATCH 14/14] build fixed --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 20672197a..2095b5a60 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,6 +35,7 @@ jobs: cd libportal git checkout f68764e meson build + cd build ninja install - name: Build # env: