├── .gitignore ├── src ├── gcav.deps.in ├── Utils │ ├── meson.build │ ├── HoverEffect.vala │ ├── HoverAction.vala │ └── MoveAction.vala ├── Widgets │ ├── meson.build │ ├── Shapes │ │ ├── meson.build │ │ ├── ShapeRectangle.vala │ │ ├── ShapeSVG.vala │ │ ├── ShapeCircle.vala │ │ └── GSVGtkShapeImage.vala │ ├── Item.vala │ ├── CanvasItem.vala │ ├── Canvas.vala │ └── ItemResizer.vala ├── namespace-info.vala.in └── meson.build ├── assets ├── GCanva.png └── GCanva.svg ├── demo ├── Screenshot.png ├── meson.build └── Window.vala ├── data ├── gcav.pc.in └── meson.build ├── Makefile ├── com.github.akiraux.libgtkcanvas.json ├── meson.build ├── docs └── meson.build ├── readme.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /src/gcav.deps.in: -------------------------------------------------------------------------------- 1 | gtk+-3.0 2 | clutter-gtk-1.0 3 | -------------------------------------------------------------------------------- /assets/GCanva.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akiraux/libgtkcanvas/HEAD/assets/GCanva.png -------------------------------------------------------------------------------- /demo/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akiraux/libgtkcanvas/HEAD/demo/Screenshot.png -------------------------------------------------------------------------------- /src/Utils/meson.build: -------------------------------------------------------------------------------- 1 | files_utils = files([ 2 | 'MoveAction.vala', 3 | 'HoverAction.vala', 4 | 'HoverEffect.vala' 5 | ]) 6 | -------------------------------------------------------------------------------- /src/Widgets/meson.build: -------------------------------------------------------------------------------- 1 | subdir('Shapes') 2 | 3 | files_widgets = files([ 4 | 'Canvas.vala', 5 | 'CanvasItem.vala', 6 | 'ItemResizer.vala', 7 | 'Item.vala' 8 | ]) 9 | -------------------------------------------------------------------------------- /src/Widgets/Shapes/meson.build: -------------------------------------------------------------------------------- 1 | files_shapes = files([ 2 | 'ShapeRectangle.vala', 3 | 'ShapeCircle.vala', 4 | ]) 5 | 6 | if svgdep.found() 7 | files_shapes += files([ 8 | 'ShapeSVG.vala', 9 | 'GSVGtkShapeImage.vala' 10 | ]) 11 | endif 12 | -------------------------------------------------------------------------------- /demo/meson.build: -------------------------------------------------------------------------------- 1 | files_demo=([ 2 | 'Window.vala' 3 | ]) 4 | 5 | args = ['--vapidir='+ldir] 6 | 7 | if svgdep.found() 8 | args+= ['--define=GSVGTK'] 9 | endif 10 | 11 | executable ('gtkcanvas-demo', 12 | files_demo, 13 | vala_args: args, 14 | dependencies : [ deps, inc_libh_dep ], 15 | link_with: lib, 16 | install: true) 17 | -------------------------------------------------------------------------------- /data/gcav.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=${prefix} 3 | libdir=@libdir@ 4 | datadir=@prefix@/share 5 | includedir=@prefix@/include 6 | 7 | Name: @PROJECT_NAME@ 8 | Description: Canvas based on Clutter and Gtk+ 3.0 9 | URL: https://github.com/Philip-Scott/libgtkcanvas 10 | Version: @VERSION@ 11 | Requires: gtk+3.0 >= 3.22.11 clutter-1.0 >= 1.26.0 12 | Libs: -L${libdir} -lgtkcanvas-@API_VERSION@ 13 | Cflags: -I${includedir}/gtkcanvas-@API_VERSION@ 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DEPS = --pkg clutter-1.0 --pkg gtk+-3.0 --pkg clutter-gtk-1.0 --pkg cairo 2 | FILES = \ 3 | src/Window.vala\ 4 | src/Widgets/Canvas.vala\ 5 | src/Widgets/CanvasItem.vala\ 6 | src/Utils/MoveAction.vala\ 7 | src/Utils/HoverAction.vala\ 8 | 9 | all: clean build docs 10 | 11 | build: 12 | valac -X -lm $(DEPS) $(FILES) -o canvasdemo 13 | docs: 14 | rm -rf gtkcanvas 15 | valadoc $(DEPS) $(FILES) -o gtkcanvas 16 | view-docs: 17 | xdg-open ./gtkcanvas/index.html 18 | clean: 19 | rm -rf gtkcanvas 20 | -------------------------------------------------------------------------------- /data/meson.build: -------------------------------------------------------------------------------- 1 | deconf = configuration_data() 2 | deconf.set('PROJECT_NAME', meson.project_name ()) 3 | deconf.set('VERSION', meson.project_version ()) 4 | deconf.set('API_VERSION', API_VERSION) 5 | deconf.set('prefix', get_option('prefix')) 6 | deconf.set('libdir', '${exec_prefix}/'+get_option('libdir')) 7 | 8 | configure_file(input : 'gcav.pc.in', 9 | output : 'gcav-@0@.pc'.format(API_VERSION), 10 | configuration : deconf, 11 | install : true, 12 | install_dir : join_paths(get_option('libdir'), 'pkgconfig') 13 | ) 14 | -------------------------------------------------------------------------------- /com.github.akiraux.libgtkcanvas.json: -------------------------------------------------------------------------------- 1 | { 2 | "app-id": "com.github.akiraux.libgtkcanvas", 3 | "runtime": "org.gnome.Platform", 4 | "runtime-version": "3.30", 5 | "sdk": "org.gnome.Sdk", 6 | "base":"io.elementary.BaseApp", 7 | "base-version": "juno", 8 | "command": "gtkcanvas-demo", 9 | "cleanup": [ 10 | "/include", 11 | "/lib/pkgconfig", 12 | "/lib/debug", 13 | "/share/vala", 14 | "/man", 15 | "*.a", 16 | "*.la" 17 | ], 18 | "finish-args": [ 19 | "--share=ipc", 20 | "--socket=wayland", 21 | "--socket=x11" 22 | ], 23 | "modules": [ 24 | { 25 | "name": "libgtkcanvas", 26 | "buildsystem": "meson", 27 | "sources": [ 28 | { 29 | "type": "git", 30 | "url": "https://github.com/akiraux/libgtkcanvas" 31 | } 32 | ] 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('gtkcanvas', [ 'vala', 'c'], version : '0.1.0') 2 | 3 | PROJECT_NAME = 'gcav' 4 | API_VERSION = '0.4' 5 | PROJECT_VERSION = meson.project_version() 6 | VERSIONED_PROJECT_NAME = PROJECT_NAME+'-'+API_VERSION 7 | CAMEL_CASE_NAME = 'Gcav' 8 | VERSIONED_CAMEL_CASE_NAME = CAMEL_CASE_NAME +'-'+ API_VERSION 9 | 10 | cc = meson.get_compiler('c') 11 | m_dep = cc.find_library('m', required: true) 12 | 13 | deps = ([dependency('gtk+-3.0', version:'>=3.18'), 14 | dependency('clutter-gtk-1.0', version:'>=1.6'), 15 | dependency('granite', version:'>=0.3'), 16 | dependency('cairo', version:'>=1.14'), 17 | m_dep 18 | ]) 19 | 20 | svgdep = dependency('gsvgtk-0.6', version:'>=0.5.0', required: false) 21 | 22 | 23 | inc_rooth = include_directories ('.') 24 | inc_rooth_dep = declare_dependency (include_directories : inc_rooth) 25 | 26 | 27 | #subdir('po') 28 | subdir('src') 29 | subdir('data') 30 | subdir('docs') 31 | subdir('demo') 32 | #subdir('tests') 33 | -------------------------------------------------------------------------------- /docs/meson.build: -------------------------------------------------------------------------------- 1 | pkgs = [ 2 | '--pkg=gtk+-3.0', 3 | '--pkg=granite', 4 | '--pkg=clutter-1.0', 5 | '--pkg=clutter-gtk-1.0' 6 | ] 7 | if svgdep.found () 8 | pkgs += ['--pkg=gsvgtk-0.6'] 9 | endif 10 | 11 | valadoc = find_program ('valadoc', required: false) 12 | if valadoc.found() 13 | outdir = CAMEL_CASE_NAME+'-'+API_VERSION 14 | gtkdoc_outdir = CAMEL_CASE_NAME+'-'+API_VERSION 15 | valacapi = run_command ('valac', '--api-version') 16 | driver = '--driver='+valacapi.stdout().strip() 17 | pkgname = '--package-name='+CAMEL_CASE_NAME+'-'+API_VERSION 18 | pkgversion = '--package-version='+PROJECT_VERSION 19 | docsdir = join_paths (get_option ('datadir'), 'devhelp','books') 20 | 21 | custom_target ('valadocs', 22 | input : sources, 23 | output : outdir, 24 | command : [valadoc, 25 | driver, 26 | '--doclet=devhelp', 27 | '--force', 28 | pkgname, 29 | pkgversion, 30 | pkgs, 31 | '--directory=@OUTDIR@', 32 | valasources], 33 | install : true, 34 | install_dir : docsdir) 35 | endif 36 | -------------------------------------------------------------------------------- /src/namespace-info.vala.in: -------------------------------------------------------------------------------- 1 | /* -*- Mode: Vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ 2 | /* 3 | * 4 | * Copyright (C) 2017 Daniel Espinosa 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | [CCode (gir_namespace = "@CAMEL_CASE_NAME@", gir_version = "@API_VERSION@", cheader_filename = "@PROJECT_NAME@.h")] 21 | namespace @CAMEL_CASE_NAME@ { 22 | } 23 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![GCanva](assets/GCanva.svg) 2 | # GCanvas 3 | 4 | This is the shared canvas specially built for [Akira](https://github.com/Alecaddd/Akira) and [Spice-Up](https://github.com/Philip-Scott/Spice-up). It will be written in Vala and use clutter for the drawing. 5 | 6 | It is meant to contain the basic elements such as a basic container class, move, resize and rotate. As well as the ability to have custom shapes and be able to drag each of their individual points. 7 | 8 | ### Dependencies: 9 | 10 | - clutter-1.0 11 | - gtk+-3.0 12 | - clutter-gtk-1.0 13 | - granite 14 | 15 | #### For SVG support 16 | 17 | - gsvgtk-0.6 18 | - librsvg-2.0 19 | - gsvg-0.4 20 | - gxml-0.16 21 | 22 | ### To compile & run the demo 23 | 24 | #### Meson Build 25 | 26 | This builds the Library, GObject Introspection, demo and documentation are build after this 27 | 28 | ``` 29 | mkdir build 30 | cd build 31 | meson .. 32 | ninja 33 | ``` 34 | 35 | #### Run demo 36 | 37 | ``` 38 | ./demo/gtkcanvas-demo 39 | ``` 40 | 41 | #### View Docs 42 | 43 | ``` 44 | xdg-open docs/GtkCanvas-0.4/GtkCanvas.html 45 | ``` 46 | 47 |

48 | Screenshot 49 |

50 | -------------------------------------------------------------------------------- /src/Utils/HoverEffect.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 3 | * 4 | * This program or library is free software; you can redistribute it 5 | * and/or modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General 15 | * Public License along with this library; if not, write to the 16 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 | * Boston, MA 02110-1301 USA. 18 | * 19 | * Authored by: Felipe Escoto 20 | */ 21 | 22 | internal class Gcav.HoverEffect : Clutter.Effect { 23 | public int border_size { get; construct; } 24 | public float scale_factor { get; set; default = 1; } 25 | 26 | Cogl.Material material; 27 | 28 | public HoverEffect (int border_size) { 29 | Object (border_size: border_size); 30 | } 31 | 32 | construct { 33 | material = new Cogl.Material (); 34 | } 35 | 36 | public override void paint (Clutter.EffectPaintFlags flags) { 37 | var bounding_box = get_bounding_box (); 38 | var color = Cogl.Color.from_4ub (65, 201, 253, 255); 39 | 40 | material.set_color (color); 41 | 42 | Cogl.set_source (material); 43 | Cogl.rectangle (bounding_box.x1, bounding_box.y1, bounding_box.x2, bounding_box.y2); 44 | 45 | actor.continue_paint (); 46 | } 47 | 48 | public virtual Clutter.ActorBox get_bounding_box () { 49 | var size = border_size * scale_factor; 50 | var bounding_box = Clutter.ActorBox (); 51 | 52 | bounding_box.set_origin (-size, -size); 53 | bounding_box.set_size (actor.width + size * 2, actor.height + size * 2); 54 | 55 | return bounding_box; 56 | } 57 | } -------------------------------------------------------------------------------- /src/Utils/HoverAction.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2 of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public 15 | * License along with this program; if not, write to the 16 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 | * Boston, MA 02110-1301 USA 18 | * 19 | * Authored by: Alessandro Castellani 20 | */ 21 | 22 | internal class Gcav.HoverAction : Object { 23 | /** 24 | * Toggle the visibility of the HoverEffect() 25 | */ 26 | public bool toggled { 27 | get { 28 | return visible; 29 | } set { 30 | if (value && effect == null) { 31 | effect = new HoverEffect (1); 32 | item.add_effect (effect); 33 | } else if (!value && effect != null) { 34 | item.remove_effect (effect); 35 | effect = null; 36 | } 37 | 38 | visible = value; 39 | } 40 | } 41 | 42 | bool visible = false; 43 | 44 | /** 45 | * Parent Item passed on mouse enter 46 | */ 47 | private unowned Item item; 48 | 49 | /** 50 | * HoverEffect (int border_width ) 51 | */ 52 | private HoverEffect? effect { get; set; default = null; } 53 | 54 | /** 55 | * Initialize Class 56 | * @param Item item [hovered item from canvas] 57 | */ 58 | public HoverAction (Item item) { 59 | this.item = item; 60 | } 61 | 62 | /** 63 | * Toggle the visibility of the bounding box 64 | * @param bool toggle 65 | * return void 66 | */ 67 | public void toggle (bool toggle) { 68 | toggled = toggle; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Widgets/Shapes/ShapeRectangle.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2 of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public 15 | * License along with this program; if not, write to the 16 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 | * Boston, MA 02110-1301 USA 18 | * 19 | * Authored by: Alessandro Castellani 20 | */ 21 | 22 | /** 23 | * ShapeRectangle is a child class of the parent CanvasItem {@link Clutter.Actor}. 24 | * 25 | * This is a specific shape class to handle the generation of a Rectangular geometry 26 | */ 27 | public class Gcav.ShapeRectangle : Gcav.CanvasItem { 28 | /** 29 | * Fill color of the shape 30 | */ 31 | public string color { 32 | get { 33 | return _color; 34 | } set { 35 | if (value == "") return; 36 | 37 | _color = value; 38 | clutter_color = Clutter.Color.from_string (value); 39 | } 40 | } 41 | private string _color; 42 | 43 | public Clutter.Color? clutter_color { 44 | get { 45 | return _clutter_color; 46 | } set { 47 | if (value == null) return; 48 | 49 | _clutter_color = value; 50 | } 51 | } 52 | private Clutter.Color _clutter_color; 53 | 54 | public ShapeRectangle (string color, double rotation) { 55 | this.color = color; 56 | this.rotation = rotation; 57 | 58 | var _canvas = new Clutter.Canvas (); 59 | _canvas.set_size (100, 100); 60 | 61 | set_rectangle (0, 0, 100, 100); 62 | 63 | _canvas.draw.connect((ctx, w, h) => { 64 | ctx.set_source_rgb (clutter_color.red, clutter_color.green, clutter_color.blue); 65 | ctx.rectangle (0, 0, w, h); 66 | ctx.fill (); 67 | 68 | return true; 69 | }); 70 | 71 | set_content (_canvas); 72 | _canvas.invalidate (); // forces the redraw 73 | } 74 | } -------------------------------------------------------------------------------- /src/Widgets/Shapes/ShapeSVG.vala: -------------------------------------------------------------------------------- 1 | /* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ 2 | /* 3 | * Copyright (c) 2018 Daniel Espinosa 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public 16 | * License along with this program; if not, write to the 17 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 | * Boston, MA 02110-1301 USA 19 | * 20 | * Authored by: Daniel Espinosa 21 | */ 22 | 23 | /** 24 | * ShapeSVG is a child class of the parent CanvasItem {@link Clutter.Actor}, 25 | * to be used to render SVG images 26 | */ 27 | using GSvg; 28 | using Rsvg; 29 | public class Gcav.ShapeSVG : Gcav.CanvasItem { 30 | private string default_image = """ 31 | 32 | 33 | No Image has been set 34 | 35 | 36 | """; 37 | public GSvg.Document svg { get; set; } 38 | 39 | public void set_svg_string (string str) throws GLib.Error { 40 | if (svg == null) svg = new GSvg.GsDocument (); 41 | svg.read_from_string (str); 42 | } 43 | construct { 44 | var _canvas = new Clutter.Canvas (); 45 | _canvas.set_size (998, 298); 46 | 47 | set_rectangle (0, 0, 100, 100); 48 | 49 | _canvas.draw.connect((ctx, w, h) => { 50 | try { 51 | if (svg == null) set_svg_string (default_image); 52 | var rsvg = new Rsvg.Handle (); 53 | rsvg.write (svg.write_string ().data); 54 | rsvg.close (); 55 | rsvg.render_cairo (ctx); 56 | } catch (GLib.Error e) { warning ("Error: %s".printf (e.message)); } 57 | return true; 58 | }); 59 | 60 | set_content (_canvas); 61 | _canvas.invalidate (); // forces the redraw 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Widgets/Shapes/ShapeCircle.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2 of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public 15 | * License along with this program; if not, write to the 16 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 | * Boston, MA 02110-1301 USA 18 | * 19 | * Authored by: Alessandro Castellani 20 | */ 21 | 22 | /** 23 | * ShapeCircle is a child class of the parent CanvasItem {@link Clutter.Actor}. 24 | * 25 | * This is a specific shape class to handle the generation of a Circle geometry 26 | */ 27 | public class Gcav.ShapeCircle : Gcav.CanvasItem { 28 | /** 29 | * Fill color of the shape 30 | */ 31 | public string color { 32 | get { 33 | return _color; 34 | } set { 35 | if (value == "") return; 36 | 37 | _color = value; 38 | clutter_color = Clutter.Color.from_string (value); 39 | } 40 | } 41 | private string _color; 42 | 43 | public Clutter.Color? clutter_color { 44 | get { 45 | return _clutter_color; 46 | } set { 47 | if (value == null) return; 48 | 49 | _clutter_color = value; 50 | } 51 | } 52 | private Clutter.Color _clutter_color; 53 | 54 | public ShapeCircle (string color, double rotation) { 55 | this.color = color; 56 | this.rotation = rotation; 57 | 58 | double angle1 = 0.0 * (Math.PI/180.0); // angles are specified 59 | double angle2 = 360.0 * (Math.PI/180.0); // in radians 60 | 61 | var _canvas = new Clutter.Canvas (); 62 | _canvas.set_size (100, 100); 63 | 64 | set_rectangle (0, 0, 100, 100); 65 | 66 | _canvas.draw.connect((ctx, w, h) => { 67 | ctx.set_source_rgb (clutter_color.red, clutter_color.green, clutter_color.blue); 68 | ctx.arc (w/2, h/2, w/2, angle1, angle2); 69 | ctx.fill (); 70 | 71 | return true; 72 | }); 73 | 74 | set_content (_canvas); 75 | _canvas.invalidate (); // forces the redraw 76 | } 77 | } -------------------------------------------------------------------------------- /src/Utils/MoveAction.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 3 | * 4 | * This program or library is free software; you can redistribute it 5 | * and/or modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General 15 | * Public License along with this library; if not, write to the 16 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 | * Boston, MA 02110-1301 USA. 18 | * 19 | * Authored by: Felipe Escoto 20 | */ 21 | 22 | /** 23 | * Manages and controls a Item's movement. 24 | */ 25 | internal class Gcav.MoveAction : Clutter.DragAction { 26 | private int x_offset_press = 0; 27 | private int y_offset_press = 0; 28 | 29 | private unowned Item item; 30 | 31 | public MoveAction (Item item) { 32 | this.item = item; 33 | 34 | drag_begin.connect (on_move_begin); 35 | drag_end.connect (on_move_end); 36 | 37 | item.add_action (this); 38 | } 39 | 40 | private void on_move_begin (Clutter.Actor actor, float event_x, float event_y, Clutter.ModifierType modifiers) { 41 | float px, py; 42 | get_press_coords (out px, out py); 43 | 44 | x_offset_press = (int)(px - item.x); 45 | y_offset_press = (int)(py - item.y); 46 | 47 | item.clicked = true; 48 | item.dragging = false; 49 | 50 | item.selected (modifiers); 51 | } 52 | 53 | protected override bool drag_progress (Clutter.Actor actor, float delta_x, float delta_y) { 54 | if (!item.clicked) { 55 | return false; 56 | } 57 | 58 | float motion_x, motion_y; 59 | get_motion_coords (out motion_x, out motion_y); 60 | 61 | var x = (int) ((motion_x - x_offset_press) / item.ratio); 62 | var y = (int) ((motion_y - y_offset_press) / item.ratio); 63 | item.set_rectangle (x, y, null, null); 64 | 65 | if (!item.dragging) { 66 | item.dragging = true; 67 | } 68 | 69 | return false; 70 | } 71 | 72 | private void on_move_end () { 73 | item.clicked = false; 74 | 75 | if (item.dragging) { 76 | item.dragging = false; 77 | } 78 | 79 | item.on_move_end (); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | subdir('Utils') 2 | subdir('Widgets') 3 | 4 | # Keep this updated to generate docs 5 | valasources=files_widgets+files_utils+files_shapes 6 | sources = files([]) 7 | 8 | vapidir = join_paths (get_option('datadir'),'vala','vapi') 9 | GIR_NAME= VERSIONED_CAMEL_CASE_NAME+'.gir' 10 | TYPELIB_NAME= VERSIONED_CAMEL_CASE_NAME+'.typelib' 11 | VAPI_NAME = VERSIONED_PROJECT_NAME+'.vapi' 12 | 13 | conf = configuration_data() 14 | conf.set('prefix', get_option('prefix')) 15 | conf.set('exec_prefix', get_option('prefix')) 16 | conf.set('libdir', join_paths (get_option ('prefix'),get_option ('libdir'))) 17 | conf.set('includedir', join_paths (get_option ('includedir'), VERSIONED_PROJECT_NAME)) 18 | conf.set('PROJECT_NAME', PROJECT_NAME) 19 | conf.set('PROJECT_VERSION', PROJECT_VERSION) 20 | conf.set('CAMEL_CASE_NAME', CAMEL_CASE_NAME) 21 | conf.set('API_VERSION', API_VERSION) 22 | 23 | configure_file(input : 'gcav.deps.in', 24 | output : 'gcav-@0@.deps'.format(API_VERSION), 25 | configuration : conf, 26 | install : true, 27 | install_dir : vapidir) 28 | 29 | nsinfo = configure_file(input : 'namespace-info.vala.in', 30 | output : 'namespace-info.vala', 31 | configuration : conf) 32 | namespaceinfo_dep = declare_dependency (sources : nsinfo) 33 | 34 | confh = configuration_data () 35 | confh.set_quoted('PACKAGE_LOCALE_DIR', join_paths(get_option('prefix'), get_option('datadir'), 'locale')) 36 | confh.set_quoted('GETTEXT_PACKAGE', PROJECT_NAME) 37 | configure_file(output : 'config.h', 38 | configuration : confh) 39 | 40 | inc_libh = include_directories ('.') 41 | inc_libh_dep = declare_dependency (include_directories : inc_libh) 42 | ldir = meson.current_source_dir() 43 | 44 | svg_args = [] 45 | if svgdep.found() 46 | deps += svgdep 47 | 48 | svg_args+= ['--define=GSVGTK'] 49 | endif 50 | 51 | 52 | # LT_VERSION for ABI related changes 53 | # From: https://autotools.io/libtool/version.html 54 | # This rules applies to Meson 0.43 55 | # Increase the current value whenever an interface has been added, removed or changed. 56 | # Always increase revision value whenever an interface has been added, removed or changed. 57 | # Increase the age value only if the changes made to the ABI are backward compatible. 58 | # Set version to the value of subtract age from current 59 | # Reset current and version to 1 and, age and version to 0 if library's name is changed 60 | LT_CURRENT='1' 61 | LT_REVISION='0' 62 | LT_AGE='0' 63 | LT_VERSION='1' 64 | lib = library(VERSIONED_PROJECT_NAME, 65 | files_utils+files_widgets+files_shapes, 66 | version : LT_VERSION, 67 | soversion : LT_VERSION+'.'+LT_AGE+'.'+LT_REVISION, 68 | vala_header : PROJECT_NAME+'.h', 69 | vala_vapi : VAPI_NAME, 70 | vala_gir : GIR_NAME, 71 | vala_args: svg_args, 72 | dependencies : [namespaceinfo_dep, deps], 73 | install : true, 74 | install_dir : [ 75 | true, 76 | join_paths (get_option('includedir'),VERSIONED_PROJECT_NAME), 77 | vapidir, 78 | true 79 | ]) 80 | 81 | 82 | g_ir_compiler = find_program('g-ir-compiler') 83 | custom_target('typelib', 84 | command: [ 85 | g_ir_compiler, 86 | '--shared-library', 'lib'+PROJECT_NAME+'-@0@.so'.format (API_VERSION), 87 | '--output', '@OUTPUT@', 88 | join_paths(meson.current_build_dir(), GIR_NAME) 89 | ], 90 | output: TYPELIB_NAME, 91 | depends: lib, 92 | install: true, 93 | install_dir: join_paths(get_option('libdir'), 'girepository-1.0')) 94 | 95 | -------------------------------------------------------------------------------- /demo/Window.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2 of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public 15 | * License along with this program; if not, write to the 16 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 | * Boston, MA 02110-1301 USA 18 | * 19 | * Authored by: Felipe Escoto 20 | */ 21 | 22 | int main (string[] argv) { 23 | GtkClutter.init (ref argv); 24 | 25 | var window = new Gtk.Window (); 26 | window.title = "GtkCanvas (Gcav) Demo"; 27 | 28 | window.resize (1000, 800); 29 | 30 | var canvas = new Gcav.Canvas (600, 400); 31 | canvas.add_shape ("rectangle", "blue", 45.0); 32 | canvas.add_shape ("rectangle", "red", 30.0); 33 | canvas.add_shape ("circle", "green", 0.0); 34 | #if GSVGTK 35 | canvas.add_shape ("svg", "blue", 0.0); 36 | #endif 37 | 38 | canvas.clicked.connect ((modifier) => { 39 | canvas.resizer.visible = false; 40 | }); 41 | 42 | var canvas_label = new Gtk.Label ("Canvas Properties"); 43 | canvas_label.get_style_context ().add_class ("h4"); 44 | 45 | var width = new Gtk.SpinButton.with_range (100, 10000, 10); 46 | width.value = canvas.width; 47 | width.value_changed.connect (() => { 48 | canvas.width = (int) width.value; 49 | }); 50 | 51 | var height = new Gtk.SpinButton.with_range (100, 10000, 10); 52 | height.value = canvas.height; 53 | height.value_changed.connect (() => { 54 | canvas.height = (int) height.value; 55 | }); 56 | 57 | var testing_grid = new Gtk.Grid (); 58 | testing_grid.orientation = Gtk.Orientation.VERTICAL; 59 | testing_grid.row_spacing = 6; 60 | 61 | var new_shape = new Gtk.Button.with_label ("Add Shape"); 62 | new_shape.clicked.connect (() => { 63 | var actor = canvas.add_shape ("rectangle", "red", 0.0); 64 | 65 | // Example on how you can add an animation 66 | actor.set_pivot_point (0.5f, 0.5f); 67 | actor.set_scale (0.01f, 0.01f); 68 | actor.opacity = 0; 69 | 70 | actor.save_easing_state (); 71 | actor.set_easing_mode (Clutter.AnimationMode.EASE_OUT_EXPO); 72 | actor.set_easing_duration (200); 73 | actor.set_scale (1.0f, 1.0f); 74 | actor.opacity = 255U; 75 | actor.restore_easing_state (); 76 | }); 77 | 78 | testing_grid.add (canvas_label); 79 | testing_grid.add (width); 80 | testing_grid.add (height); 81 | testing_grid.add (new_shape); 82 | 83 | var main_grid = new Gtk.Grid (); 84 | main_grid.margin = 6; 85 | main_grid.column_spacing = 6; 86 | main_grid.orientation = Gtk.Orientation.HORIZONTAL; 87 | 88 | var separator = new Gtk.Separator (Gtk.Orientation.VERTICAL); 89 | 90 | main_grid.add (canvas); 91 | main_grid.add (separator); 92 | main_grid.add (testing_grid); 93 | 94 | window.add (main_grid); 95 | 96 | window.destroy.connect (Gtk.main_quit); 97 | window.show_all (); 98 | Gtk.main (); 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /src/Widgets/Shapes/GSVGtkShapeImage.vala: -------------------------------------------------------------------------------- 1 | /* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */ 2 | /* 3 | * Copyright (c) 2018 Daniel Espinosa 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public 16 | * License along with this program; if not, write to the 17 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 | * Boston, MA 02110-1301 USA 19 | * 20 | * Authored by: Daniel Espinosa 21 | */ 22 | 23 | /** 24 | * GSVGtk Image Shape is a child class of the parent CanvasItem {@link Clutter.Actor}, 25 | * to be used to render SVG images 26 | */ 27 | using GSvg; 28 | using Rsvg; 29 | public class Gcav.GSvgtkShapeImage : GSvgtk.ActorImage, Gcav.Item { 30 | private MoveAction move_action; 31 | private HoverAction hover_action; 32 | 33 | public bool on_hover_effect { 34 | get { 35 | return _on_hover_effect; 36 | } set { 37 | if (!value) { 38 | hover_action.toggle (false); 39 | } 40 | 41 | _on_hover_effect = value; 42 | } 43 | } 44 | private bool _on_hover_effect = true; 45 | 46 | public bool dragging { get; internal set; default = false; } 47 | 48 | public bool clicked { get; internal set; default = false; } 49 | 50 | public float real_x { 51 | get { 52 | return _real_x; 53 | } set { 54 | _real_x = value; 55 | apply_ratio (ratio); 56 | } 57 | } 58 | private float _real_x; 59 | 60 | public float real_y { 61 | get { 62 | return _real_y; 63 | } set { 64 | _real_y = value; 65 | apply_ratio (ratio); 66 | } 67 | } 68 | private float _real_y; 69 | 70 | public float real_w { 71 | get { 72 | return _real_w; 73 | } set { 74 | _real_w = value; 75 | apply_ratio (ratio); 76 | } 77 | } 78 | private float _real_w; 79 | 80 | public float real_h { 81 | get { 82 | return _real_h; 83 | } set { 84 | _real_h = value; 85 | apply_ratio (ratio); 86 | } 87 | } 88 | private float _real_h; 89 | 90 | public double rotation { 91 | get { 92 | return _rotation; 93 | } set { 94 | if (value < 0) return; 95 | 96 | _rotation = value % 360; 97 | rotation_angle_z = value % 360; 98 | updated (); 99 | } 100 | } 101 | private double _rotation = 0.0; 102 | public float ratio { get; set; } 103 | 104 | construct { 105 | reactive = true; 106 | set_pivot_point (0.5f, 0.5f); 107 | 108 | move_action = new MoveAction (this); 109 | hover_action = new HoverAction (this); 110 | 111 | enter_event.connect (() => { 112 | if (on_hover_effect) { 113 | hover_action.toggle (true); 114 | } 115 | }); 116 | 117 | leave_event.connect (() => { 118 | if (on_hover_effect) { 119 | hover_action.toggle (false); 120 | } 121 | }); 122 | } 123 | 124 | public GSvgtkShapeImage.with_values (float x, float y, float w, float h, string color) { 125 | Object (background_color: Clutter.Color.from_string (color)); 126 | set_rectangle (x, y, w, h); 127 | } 128 | 129 | public GSvgtkShapeImage () { 130 | set_rectangle (0, 0, 100, 100); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/Widgets/Item.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 <@> 3 | * Copyright (C) 2018 Daniel Espinosa 4 | * 5 | * This program or library is free software; you can redistribute it 6 | * and/or modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General 16 | * Public License along with this library; if not, write to the 17 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 | * Boston, MA 02110-1301 USA. 19 | * 20 | * Authored by: Felipe Escoto 21 | */ 22 | 23 | /** 24 | * CanvasItem is a {@link Clutter.Actor} which is the base class of all items that can appear on the Canvas. 25 | * 26 | * This class should take care of the basics such as dragging, clicks and rotation, 27 | * and leave more specific implementations to child classes. 28 | */ 29 | public interface Gcav.Item : Clutter.Actor { 30 | /** 31 | * Signal triggered when this is selected by the user. 32 | * 33 | * @param modifiers this is a mask that contains all the modifiers for the event such as if Shift/Ctrl were pressed, or which button on the mouse was clicked 34 | */ 35 | public signal void selected (Clutter.ModifierType modifiers); 36 | 37 | /** 38 | * Signal triggered when the rectangle of this changes 39 | */ 40 | public signal void updated (); 41 | 42 | /** 43 | * Triggered after a move operation, when the mouse button is lifted 44 | */ 45 | public signal void on_move_end (); 46 | 47 | /** 48 | * Sets if the hover effect should appear when you hover on this. Disabling the effect if currently visible; 49 | */ 50 | public abstract bool on_hover_effect { get; set; } 51 | /** 52 | * True if this is currently being dragged 53 | */ 54 | public abstract bool dragging { get; internal set; } 55 | /** 56 | * True if this was just clicked, but not yet moved 57 | */ 58 | public abstract bool clicked { get; internal set; } 59 | /** 60 | * Sets the position x of this shape on the canvas. 61 | * 62 | * Doing this causes an update on the aspect ratio. So it's better to use set_rectangle 63 | */ 64 | public abstract float real_x { get; set; } 65 | /** 66 | * Sets the position y of this shape on the canvas. 67 | * 68 | * Doing this causes an update on the aspect ratio. So it's better to use set_rectangle 69 | */ 70 | public abstract float real_y { get; set; } 71 | /** 72 | * Sets the height of this shape on the canvas. 73 | * 74 | * Doing this causes an update on the aspect ratio. So it's better to use set_rectangle 75 | */ 76 | public abstract float real_w { get; set; } 77 | /** 78 | * Sets the width of this shape on the canvas. 79 | * 80 | * Doing this causes an update on the aspect ratio. So it's better to use set_rectangle 81 | */ 82 | public abstract float real_h { get; set; } 83 | /** 84 | * The item's rotation. From 0 to 360 degrees 85 | */ 86 | public abstract double rotation { get; set; } 87 | /** 88 | * The item's ratio to scale size 89 | */ 90 | public abstract float ratio { get; set; } 91 | /** 92 | * Set's the coordenates and size of this, ignoring nulls. Use this to set multiple "real_n" properties without causing uneeded updates. 93 | */ 94 | public virtual void set_rectangle (float? x, float? y, float? w, float? h) { 95 | if (x != null) { 96 | real_x = x; 97 | } 98 | 99 | if (y != null) { 100 | real_y = y; 101 | } 102 | 103 | if (w != null) { 104 | real_w = w; 105 | } 106 | 107 | if (h != null) { 108 | real_h = h; 109 | } 110 | 111 | apply_ratio (ratio); 112 | } 113 | 114 | internal void apply_ratio (float ratio) { 115 | this.ratio = ratio; 116 | 117 | width = (float) (real_w * ratio); 118 | height = (float) (real_h * ratio); 119 | x = (float) (real_x * ratio); 120 | y = (float) (real_y * ratio); 121 | 122 | updated (); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /assets/GCanva.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | GCanva 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/Widgets/CanvasItem.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 3 | * 4 | * This program or library is free software; you can redistribute it 5 | * and/or modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General 15 | * Public License along with this library; if not, write to the 16 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 | * Boston, MA 02110-1301 USA. 18 | * 19 | * Authored by: Felipe Escoto 20 | */ 21 | 22 | /** 23 | * CanvasItem is a {@link Clutter.Actor} which is the base class of all items that can appear on the Canvas. 24 | * 25 | * This class should take care of the basics such as dragging, clicks and rotation, 26 | * and leave more specific implementations to child classes. 27 | */ 28 | public class Gcav.CanvasItem : Clutter.Actor, Gcav.Item { 29 | 30 | private MoveAction move_action; 31 | private HoverAction hover_action; 32 | 33 | /** 34 | * Sets if the hover effect should appear when you hover on this. Disabling the effect if currently visible; 35 | */ 36 | public bool on_hover_effect { 37 | get { 38 | return _on_hover_effect; 39 | } set { 40 | if (!value) { 41 | hover_action.toggle (false); 42 | } 43 | 44 | _on_hover_effect = value; 45 | } 46 | } 47 | private bool _on_hover_effect = true; 48 | 49 | /** 50 | * True if this is currently being dragged 51 | */ 52 | public bool dragging { get; internal set; default = false; } 53 | 54 | /** 55 | * True if this was just clicked, but not yet moved 56 | */ 57 | public bool clicked { get; internal set; default = false; } 58 | 59 | /** 60 | * Sets the position x of this shape on the canvas. 61 | * 62 | * Doing this causes an update on the aspect ratio. So it's better to use set_rectangle 63 | */ 64 | public float real_x { 65 | get { 66 | return _real_x; 67 | } set { 68 | _real_x = value; 69 | apply_ratio (ratio); 70 | } 71 | } 72 | private float _real_x; 73 | 74 | /** 75 | * Sets the position y of this shape on the canvas. 76 | * 77 | * Doing this causes an update on the aspect ratio. So it's better to use set_rectangle 78 | */ 79 | public float real_y { 80 | get { 81 | return _real_y; 82 | } set { 83 | _real_y = value; 84 | apply_ratio (ratio); 85 | } 86 | } 87 | private float _real_y; 88 | 89 | /** 90 | * Sets the height of this shape on the canvas. 91 | * 92 | * Doing this causes an update on the aspect ratio. So it's better to use set_rectangle 93 | */ 94 | public float real_w { 95 | get { 96 | return _real_w; 97 | } set { 98 | _real_w = value; 99 | apply_ratio (ratio); 100 | } 101 | } 102 | private float _real_w; 103 | 104 | /** 105 | * Sets the width of this shape on the canvas. 106 | * 107 | * Doing this causes an update on the aspect ratio. So it's better to use set_rectangle 108 | */ 109 | public float real_h { 110 | get { 111 | return _real_h; 112 | } set { 113 | _real_h = value; 114 | apply_ratio (ratio); 115 | } 116 | } 117 | private float _real_h; 118 | 119 | /** 120 | * The item's rotation. From 0 to 360 degrees 121 | */ 122 | public double rotation { 123 | get { 124 | return _rotation; 125 | } set { 126 | if (value < 0) return; 127 | 128 | _rotation = value % 360; 129 | rotation_angle_z = value % 360; 130 | updated (); 131 | } 132 | } 133 | private double _rotation = 0.0; 134 | 135 | /** 136 | * Ratio relative to the container to properly scale all the elements 137 | */ 138 | public float ratio { get; set; default = 1.0f; } 139 | 140 | public CanvasItem.with_values (float x, float y, float w, float h, string color) { 141 | Object (background_color: Clutter.Color.from_string (color)); 142 | set_rectangle (x, y, w, h); 143 | } 144 | 145 | public CanvasItem () { 146 | set_rectangle (0, 0, 100, 100); 147 | } 148 | 149 | construct { 150 | reactive = true; 151 | set_pivot_point (0.5f, 0.5f); 152 | 153 | move_action = new MoveAction (this); 154 | hover_action = new HoverAction (this); 155 | 156 | enter_event.connect (() => { 157 | if (on_hover_effect) { 158 | hover_action.toggle (true); 159 | } 160 | }); 161 | 162 | leave_event.connect (() => { 163 | if (on_hover_effect) { 164 | hover_action.toggle (false); 165 | } 166 | }); 167 | } 168 | 169 | /** 170 | * Set's the coordenates and size of this, ignoring nulls. Use this to set multiple "real_n" properties without causing uneeded updates. 171 | */ 172 | public void set_rectangle (float? x, float? y, float? w, float? h) { 173 | if (x != null) { 174 | _real_x = x; 175 | } 176 | 177 | if (y != null) { 178 | _real_y = y; 179 | } 180 | 181 | if (w != null) { 182 | _real_w = w; 183 | } 184 | 185 | if (h != null) { 186 | _real_h = h; 187 | } 188 | 189 | apply_ratio (ratio); 190 | } 191 | 192 | internal void apply_ratio (float ratio) { 193 | this.ratio = ratio; 194 | 195 | width = (real_w * ratio); 196 | height = (real_h * ratio); 197 | x = (real_x * ratio); 198 | y = (real_y * ratio); 199 | 200 | updated (); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/Widgets/Canvas.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 3 | * 4 | * This program or library is free software; you can redistribute it 5 | * and/or modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General 15 | * Public License along with this library; if not, write to the 16 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 | * Boston, MA 02110-1301 USA. 18 | * 19 | * Authored by: Felipe Escoto 20 | */ 21 | 22 | /** 23 | * This is a widget that holds and renders {@link Gcav.Item} and their subclasses 24 | * 25 | * This class should take care of zoom-in/out, and maintaing the aspect ratio of this and it's Items when the canvas is resized. 26 | */ 27 | public class Gcav.Canvas : Gtk.AspectFrame { 28 | /** 29 | * Signal triggered when a {@link Gcav.Item} on this canvas is selected by the user. 30 | * 31 | * @param item The canvas item which triggered the event. 32 | * @param modifiers A mask that contains all the modifiers for the event such as if Shift/Ctrl were pressed, or which button on the mouse was clicked. 33 | */ 34 | public signal void item_selected (Item item, Clutter.ModifierType modifiers); 35 | 36 | /** 37 | * Signal triggered when the canvas is clicked, but not any of the items in this 38 | */ 39 | public signal void clicked (Clutter.ModifierType modifiers); 40 | 41 | private List items; 42 | 43 | private int current_allocated_width; 44 | private float current_ratio = 1.0f; 45 | 46 | private GtkClutter.Embed stage; 47 | 48 | /** 49 | * The resizer the {@link Gcav.Item}s in this canvas will use. 50 | * 51 | * Can be overwritten to make the items use a different style of resizer. 52 | */ 53 | public ItemResizer resizer { get; protected set; } 54 | 55 | /** 56 | * This value controls the zoom level the items will use. 57 | * A value of 0.5 will make items half as big, while a value of 2.0 make them twice as large 58 | * 59 | * Defaults to 1.0, and must be larger than 0. Currently does not do anything until scrolling gets implemented 60 | */ 61 | public float zoom_level { 62 | get { 63 | return _zoom_level; 64 | } set { 65 | if (value < 1) return; 66 | 67 | //_zoom_level = value; 68 | update_current_ratio (); 69 | } 70 | } 71 | private float _zoom_level = 1.0f; 72 | 73 | /** 74 | * The "virtual" width of the canvas. This is the size in pixels that the canvas will represent. 75 | * 76 | * Defaults to 100, and must be larger than 0 77 | */ 78 | public int width { 79 | get { 80 | return _width; 81 | } set { 82 | if (value < 1) return; 83 | 84 | _width = value; 85 | ratio = (float) width / height; 86 | update_current_ratio (); 87 | } 88 | } 89 | private int _width = 100; 90 | 91 | /** 92 | * The "virtual" height of the canvas. This is the size in pixels that the canvas will represent. 93 | * 94 | * Defaults to 100, and must be larger than 0 95 | */ 96 | public int height { 97 | get { 98 | return _height; 99 | } set { 100 | if (value < 1) return; 101 | 102 | _height = value; 103 | ratio = (float) width / height; 104 | update_current_ratio (); 105 | } 106 | } 107 | private int _height = 100; 108 | 109 | /** 110 | * Creates a new {@link Gcav.Canvas}. 111 | * 112 | * @param width the width in px the canvas will represent 113 | * @param height the height in px the canvas will represent 114 | */ 115 | public Canvas (int width, int height) { 116 | Object (width: width, height: height, obey_child: false); 117 | } 118 | 119 | private bool item_clicked = false; 120 | 121 | construct { 122 | stage = new GtkClutter.Embed (); 123 | stage.set_use_layout_size (false); 124 | 125 | var actor = stage.get_stage (); 126 | 127 | items = new List(); 128 | resizer = new ItemResizer (actor); 129 | 130 | var drag_action = new Clutter.DragAction (); 131 | actor.add_action (drag_action); 132 | 133 | drag_action.drag_end.connect ((a, x, y, modifiers) => { 134 | if (!item_clicked) { 135 | clicked (modifiers); 136 | } 137 | 138 | item_clicked = false; 139 | }); 140 | 141 | resizer.resize_start.connect (() => { 142 | item_clicked = true; 143 | }); 144 | 145 | item_selected.connect (() => { 146 | item_clicked = true; 147 | }); 148 | 149 | add (stage); 150 | } 151 | 152 | /** 153 | * Adds a test shape. Great for testing the library! 154 | * 155 | * @param type the shape to be generated [rectangle, circle] 156 | * @param color the color the test-shape will be, in CSS format 157 | * @param rotation the amount of degrees the item will be rotated 158 | */ 159 | public Item add_shape (string type, string color, double rotation) { 160 | Item item; 161 | 162 | switch (type) { 163 | case "rectangle": 164 | item = new ShapeRectangle (color, rotation); 165 | break; 166 | case "circle": 167 | item = new ShapeCircle (color, rotation); 168 | break; 169 | #if GSVGTK 170 | case "svg": 171 | item = new GSvgtkShapeImage (); 172 | break; 173 | #endif 174 | default: 175 | item = new ShapeRectangle (color, rotation); 176 | break; 177 | } 178 | 179 | add_item (item); 180 | return item; 181 | } 182 | 183 | /** 184 | * Adds a {@link Item} to this 185 | * 186 | * @param item the canvas item to be added 187 | */ 188 | public void add_item (Item item) { 189 | items.prepend (item); 190 | stage.get_stage ().add_child (item); 191 | item.apply_ratio (current_ratio); 192 | 193 | item.selected.connect ((modifiers) => { 194 | item_selected (item, modifiers); 195 | resizer.select_item (item); 196 | }); 197 | } 198 | 199 | /** 200 | * Removes a {@link Item} from this 201 | * 202 | * @param item the canvas item to be removed 203 | */ 204 | public void remove_item (Item item) { 205 | items.remove (item); 206 | stage.get_stage ().remove_child (item); 207 | } 208 | 209 | /** 210 | * Returns a read-only list of all the {@link Gcav.Item}s on this canvas 211 | */ 212 | public List get_items () { 213 | return items.copy (); 214 | } 215 | 216 | private void update_current_ratio () { 217 | current_allocated_width = stage.get_allocated_width (); 218 | if (current_allocated_width < 0) return; 219 | 220 | current_ratio = ((float) (current_allocated_width) / width) * zoom_level; 221 | 222 | foreach (var item in items) { 223 | item.apply_ratio (current_ratio); 224 | } 225 | } 226 | 227 | /** 228 | * For internal usage 229 | */ 230 | public override bool draw (Cairo.Context cr) { 231 | if (current_allocated_width != stage.get_allocated_width ()) { 232 | update_current_ratio (); 233 | } 234 | 235 | return base.draw (cr); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /src/Widgets/ItemResizer.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 3 | * 4 | * This program or library is free software; you can redistribute it 5 | * and/or modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General 15 | * Public License along with this library; if not, write to the 16 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 | * Boston, MA 02110-1301 USA. 18 | * 19 | * Authored by: Felipe Escoto 20 | */ 21 | 22 | /** 23 | * The grabbers needed to resize and rotate single item on the canvas 24 | * 25 | * TODO: Rotation snapping to closest 5 degree 26 | */ 27 | public class Gcav.ItemResizer { 28 | /** 29 | * Signal triggered when a resize is starting 30 | */ 31 | public signal void resize_start (); 32 | 33 | private const int SIZE = 10; 34 | private const float OFFSET = 5; 35 | 36 | private unowned Clutter.Actor canvas_actor; 37 | private unowned Gcav.Item? item = null; 38 | private bool updating = false; 39 | 40 | private Gcav.Item grabber[9]; 41 | private int selected_id = -1; 42 | 43 | /** 44 | * Sets whether the resize controls on the canvas are visible or not 45 | */ 46 | public bool visible { 47 | set { 48 | for (int i = 0; i < 9; i++) { 49 | grabber[i].visible = value; 50 | } 51 | } 52 | } 53 | 54 | /** 55 | * Sets whether the resize controls on the canvas will appear or not 56 | */ 57 | public bool enabled { 58 | get { 59 | return _enabled; 60 | } set { 61 | _enabled = value; 62 | if (!value) { 63 | for (int i = 0; i < 9; i++) { 64 | grabber[i].visible = false; 65 | } 66 | } 67 | } 68 | } 69 | private bool _enabled = true; 70 | 71 | /** 72 | * Creates the widgets needed to re-size {@link Gcav.Item}. 73 | * 74 | * @param actor The actor from a {@link Gcav.Canvas}. 75 | */ 76 | public ItemResizer (Clutter.Actor actor) { 77 | canvas_actor = actor; 78 | 79 | for (int i = 0; i < 9; i++) { 80 | grabber[i] = make_grabber (i); 81 | } 82 | } 83 | 84 | /** 85 | * Override this function if you want to style the grabbers different. 86 | * 87 | * @param id the ID of the item we're requesting. From 0 - 8, clockwize starting at the top left corner, with 9 being the rotator 88 | * @return a {@link Gcav.Item} or subclass of it 89 | */ 90 | public virtual Gcav.Item create_grabber (int id) { 91 | // TODO: Make a better shape for the grabbers 92 | if (id == 8) { 93 | return new Gcav.CanvasItem.with_values (0, 0, SIZE, SIZE, "blue"); 94 | } else { 95 | return new Gcav.CanvasItem.with_values (0, 0, SIZE, SIZE, "black"); 96 | } 97 | } 98 | 99 | private Gcav.Item make_grabber (int id) { 100 | var g = create_grabber (id); 101 | g.visible = false; 102 | canvas_actor.add (g); 103 | 104 | g.selected.connect (() => { 105 | selected_id = id; 106 | resize_start (); 107 | }); 108 | 109 | g.updated.connect (() => { 110 | if (!updating) { 111 | resize (id); 112 | } 113 | }); 114 | 115 | g.on_move_end.connect (() => { 116 | selected_id = -1; 117 | update (); 118 | }); 119 | 120 | return g; 121 | } 122 | 123 | /** 124 | * Positions the resize grabbers around a {@link Gcav.Item} 125 | * 126 | * @param item the canvas item it will position around 127 | */ 128 | public void select_item (Gcav.Item item) { 129 | if (!enabled) return; 130 | 131 | for (int i = 0; i < 9; i++) { 132 | canvas_actor.set_child_above_sibling (grabber[i], null); 133 | } 134 | 135 | if (this.item != null) { 136 | this.item.updated.disconnect (update); 137 | } 138 | 139 | this.item = item; 140 | item.updated.connect (update); 141 | selected_id = -1; 142 | visible = true; 143 | update (); 144 | } 145 | 146 | /* 147 | * Updates the positioning of all the grabbers on an item 148 | */ 149 | private void update () { 150 | if (item == null) return; 151 | updating = true; 152 | 153 | var cx = item.x + item.width / 2; 154 | var cy = item.y + item.height / 2; 155 | 156 | var radians = to_radians (item.rotation); 157 | 158 | var _sin = Math.sin (radians); 159 | var _cos = Math.cos (radians); 160 | 161 | if (selected_id != 0) { 162 | var x = item.x; 163 | var y = item.y; 164 | 165 | var xf = get_rot_x (x, cx, y, cy, _sin, _cos); 166 | var yf = get_rot_y (x, cx, y, cy, _sin, _cos); 167 | 168 | grabber[0].set_rectangle (xf - OFFSET, yf - OFFSET, null, null); 169 | } 170 | 171 | if (selected_id != 1) { 172 | var x = item.x + item.width / 2; 173 | var y = item.y; 174 | 175 | var xf = get_rot_x (x, cx, y, cy, _sin, _cos); 176 | var yf = get_rot_y (x, cx, y, cy, _sin, _cos); 177 | 178 | grabber[1].set_rectangle (xf - OFFSET, yf - OFFSET, null, null); 179 | } 180 | 181 | if (selected_id != 2) { 182 | var x = item.x + item.width; 183 | var y = item.y; 184 | 185 | var xf = get_rot_x (x, cx, y, cy, _sin, _cos); 186 | var yf = get_rot_y (x, cx, y, cy, _sin, _cos); 187 | 188 | grabber[2].set_rectangle (xf - OFFSET, yf - OFFSET, null, null); 189 | } 190 | 191 | if (selected_id != 3) { 192 | var x = item.x + item.width; 193 | var y = item.y + item.height / 2; 194 | 195 | var xf = get_rot_x (x, cx, y, cy, _sin, _cos); 196 | var yf = get_rot_y (x, cx, y, cy, _sin, _cos); 197 | 198 | grabber[3].set_rectangle (xf - OFFSET, yf - OFFSET, null, null); 199 | } 200 | 201 | if (selected_id != 4) { 202 | var x = item.x + item.width; 203 | var y = item.y + item.height; 204 | 205 | var xf = get_rot_x (x, cx, y, cy, _sin, _cos); 206 | var yf = get_rot_y (x, cx, y, cy, _sin, _cos); 207 | 208 | grabber[4].set_rectangle (xf - OFFSET, yf - OFFSET, null, null); 209 | } 210 | 211 | if (selected_id != 5) { 212 | var x = item.x + item.width / 2; 213 | var y = item.y + item.height; 214 | 215 | var xf = get_rot_x (x, cx, y, cy, _sin, _cos); 216 | var yf = get_rot_y (x, cx, y, cy, _sin, _cos); 217 | 218 | grabber[5].set_rectangle (xf - OFFSET, yf - OFFSET, null, null); 219 | } 220 | 221 | if (selected_id != 6) { 222 | var x = item.x; 223 | var y = item.y + item.height; 224 | 225 | var xf = get_rot_x (x, cx, y, cy, _sin, _cos); 226 | var yf = get_rot_y (x, cx, y, cy, _sin, _cos); 227 | 228 | grabber[6].set_rectangle (xf - OFFSET, yf - OFFSET, null, null); 229 | } 230 | 231 | if (selected_id != 7) { 232 | var x = item.x; 233 | var y = item.y + item.height / 2 ; 234 | 235 | var xf = get_rot_x (x, cx, y, cy, _sin, _cos); 236 | var yf = get_rot_y (x, cx, y, cy, _sin, _cos); 237 | 238 | grabber[7].set_rectangle (xf - OFFSET, yf - OFFSET, null, null); 239 | } 240 | 241 | if (selected_id != 8) { 242 | var x = item.x + item.width / 2; 243 | var y = item.y - 48; 244 | 245 | var xf = get_rot_x (x, cx, y, cy, _sin, _cos); 246 | var yf = get_rot_y (x, cx, y, cy, _sin, _cos); 247 | 248 | grabber[8].set_rectangle (xf - OFFSET, yf - OFFSET, null, null); 249 | } 250 | 251 | updating = false; 252 | } 253 | 254 | /* 255 | * Gets the x position of point A {x,y} on a centroid {cx, cy}. 256 | */ 257 | inline float get_rot_x (double x, double cx, double y, double cy, double sin, double cos) { 258 | return (float) ((x - cx) * cos - (y - cy) * sin + cx); 259 | } 260 | 261 | /* 262 | * Gets the y position of point A {x,y} on a centroid {cx, cy}. 263 | */ 264 | inline float get_rot_y (double x, double cx, double y, double cy, double sin, double cos) { 265 | return (float) ((x - cx) * sin + (y - cy) * cos + cy); 266 | } 267 | 268 | inline double to_radians (double degrees) { 269 | return degrees / (180.0 / Math.PI); 270 | } 271 | 272 | inline float to_deg (float rad) { 273 | return rad * (180.0f / (float) Math.PI); 274 | } 275 | 276 | /* 277 | * Depending on the grabbed grabber & current rotation, resize the item acordingly 278 | * 279 | * Dear future self: 280 | * 281 | * If you're looking at this file because the item resizing broke, consider it not fixable. 282 | * Nope, don't even try it. Just rewrite it. And re-do all the maths. 283 | */ 284 | private void resize (int id) { 285 | float x, y; 286 | 287 | var cx = item.x + item.width / 2.0f; 288 | var cy = item.y + item.height / 2.0f; 289 | 290 | var rad = to_radians (-1f * item.rotation); 291 | var radians = to_radians (item.rotation); 292 | 293 | var _sin = (float) Math.sin (rad); 294 | var _cos = (float) Math.cos (rad); 295 | 296 | x = get_rot_x (grabber[id].x, cx, grabber[id].y, cy, _sin, _cos); 297 | y = get_rot_y (grabber[id].x, cx, grabber[id].y, cy, _sin, _cos); 298 | 299 | _sin = (float) Math.sin (radians); 300 | _cos = (float) Math.cos (radians); 301 | 302 | switch (id) { 303 | case 0: 304 | float new_width = item.width + item.x - x - OFFSET; 305 | float new_height = item.height + item.y - y - OFFSET; 306 | 307 | float delta_h = new_height - item.height; 308 | float delta_w = new_width - item.width; 309 | 310 | var new_x = (delta_w * (_cos - 1.0f)) / 2.0f - (delta_h * _sin / 2f); 311 | var new_y = (delta_w * _sin / 2.0f) + delta_h * (_cos - 1) / 2f; 312 | 313 | item.set_rectangle ( 314 | (x - new_x + OFFSET) / (item.ratio), 315 | (y - new_y + OFFSET) / (item.ratio), 316 | new_width / item.ratio, 317 | new_height / item.ratio 318 | ); 319 | 320 | break; 321 | case 1: 322 | float new_height = item.height + item.y - y - OFFSET; 323 | 324 | float delta_h = new_height - item.height; 325 | 326 | var new_x = item.x + (delta_h * _sin / 2); 327 | var new_y = delta_h * (_cos - 1) / 2; 328 | 329 | item.set_rectangle ( 330 | new_x / item.ratio, 331 | (y - new_y + OFFSET) / item.ratio, 332 | null, 333 | new_height / item.ratio 334 | ); 335 | 336 | break; 337 | case 2: 338 | float new_width = x - item.x + OFFSET; 339 | float new_height = item.height + item.y - y - OFFSET; 340 | 341 | float delta_w = new_width - item.width; 342 | float delta_h = new_height - item.height; 343 | 344 | var new_x = item.x + (delta_h * _sin / 2) + (delta_w * (_cos - 1) / 2); 345 | var new_y = delta_h * (_cos - 1) / 2 - delta_w * _sin / 2; 346 | 347 | item.set_rectangle ( 348 | new_x / item.ratio, 349 | (y - new_y + OFFSET) / item.ratio, 350 | new_width / item.ratio, 351 | new_height / item.ratio 352 | ); 353 | 354 | break; 355 | case 3: 356 | float new_width = x - item.x + OFFSET; 357 | 358 | float delta_w = new_width - item.width; 359 | 360 | var new_x = item.x + (delta_w * (_cos - 1) / 2); 361 | var new_y = item.y + delta_w * _sin / 2; 362 | 363 | item.set_rectangle ( 364 | new_x / item.ratio, 365 | new_y / item.ratio, 366 | new_width / item.ratio, 367 | null 368 | ); 369 | 370 | break; 371 | case 4: 372 | float new_width = x - item.x + OFFSET; 373 | float new_height = y - item.y + OFFSET; 374 | 375 | float delta_w = new_width - item.width; 376 | float delta_h = new_height - item.height; 377 | 378 | var new_x = item.x + (delta_w * (_cos - 1) / 2) - (delta_h * _sin) / 2; 379 | var new_y = item.y + delta_w * _sin / 2 + delta_h * (_cos - 1) / 2; 380 | 381 | item.set_rectangle ( 382 | new_x / item.ratio, 383 | new_y / item.ratio, 384 | new_width / item.ratio, 385 | new_height / item.ratio 386 | ); 387 | 388 | break; 389 | case 5: 390 | float new_height = y - item.y + OFFSET; 391 | 392 | float delta_h = new_height - item.height; 393 | 394 | var new_x = item.x - (delta_h * _sin / 2f); 395 | var new_y = item.y + delta_h * (_cos - 1) / 2f; 396 | 397 | item.set_rectangle ( 398 | new_x / item.ratio, 399 | new_y / item.ratio, 400 | null, 401 | new_height / item.ratio 402 | ); 403 | 404 | break; 405 | case 6: 406 | float new_width = item.width + item.x - x - OFFSET; 407 | float new_height = y - item.y + OFFSET; 408 | 409 | float delta_h = new_height - item.height; 410 | float delta_w = new_width - item.width; 411 | 412 | var new_x = x - (delta_h * _sin / 2f) - (delta_w * (_cos - 1.0f)) / 2.0f + OFFSET; 413 | var new_y = item.y - (delta_w * _sin) / 2.0f + delta_h * (_cos - 1f) / 2f; 414 | 415 | item.set_rectangle ( 416 | new_x / item.ratio, 417 | new_y / item.ratio, 418 | new_width / item.ratio, 419 | new_height / item.ratio 420 | ); 421 | 422 | break; 423 | case 7: 424 | float new_width = item.width + item.x - x - OFFSET; 425 | 426 | float delta_w = new_width - item.width; 427 | 428 | var new_x = x - (delta_w * (_cos - 1.0f) ) / 2.0f + OFFSET; 429 | var new_y = item.y - (delta_w * _sin) / 2.0f; 430 | 431 | item.set_rectangle ( 432 | new_x / item.ratio, 433 | new_y / item.ratio, 434 | new_width / item.ratio, 435 | null 436 | ); 437 | 438 | break; 439 | case 8: 440 | var center_x = item.x + item.width / 2.0f; 441 | var center_y = item.y + item.height / 2.0f; 442 | 443 | item.rotation = 180f - to_deg (Math.atan2f (grabber[id].x - center_x, grabber[id].y - center_y)); 444 | break; 445 | } 446 | } 447 | } 448 | --------------------------------------------------------------------------------