Commit 46f1099
Bird UI - a new GUI toolkit based on SVG components
Changed files (25)
modifiedbirdfont/GtkWindow.valamodifiedbirdfont/Main.valaaddedbirdui/AbsoluteLayout.valaaddedbirdui/BoxLayout.valaaddedbirdui/Component.valaaddedbirdui/GtkWidget.valaaddedbirdui/GtkWindow.valaaddedbirdui/HBox.valaaddedbirdui/Style.valaaddedbirdui/SvgComponent.valaaddedbirdui/VBox.valamodifieddodo.pymodifiedlibsvgbird/AttributePattern.valamodifiedlibsvgbird/Color.valamodifiedlibsvgbird/Defs.valamodifiedlibsvgbird/Object.valamodifiedlibsvgbird/Points.valamodifiedlibsvgbird/Selector.valamodifiedlibsvgbird/SelectorPattern.valamodifiedlibsvgbird/SelectorTag.valamodifiedlibsvgbird/StyleSheet.valamodifiedlibsvgbird/SvgArc.valamodifiedlibsvgbird/SvgFile.valamodifiedlibsvgbird/SvgPath.valamodifiedlibsvgbird/SvgStyle.vala
Diff
@@ -52,8 +52,6 @@ public class GtkWindow : Gtk.Window, NativeWindow { Box text_box; Gtk.Button submit_text_button; - Gtk.Window tooltip_window = new Gtk.Window ();- ToolboxCanvas toolbox; Task background_task = new Task(idle);@@ -112,12 +110,7 @@ public class GtkWindow : Gtk.Window, NativeWindow { uri = Preview.get_uri (); html = Preview.get_html_with_absolute_paths (); - try { - html_canvas.load_html_string (html, uri);- } catch (Error e) {- warning (e.message);- warning ("Failed to load html into canvas.");- }+ html_canvas.load_html_string (html, uri); // show the webview when loading has finished html_box.set_visible (true); @@ -32,7 +32,7 @@ public static int main (string[] arg) { window.set_native (native_window); native_window.init (); - birdfont.load_font_from_command_line ();+ BirdFont.BirdFont.load_font_from_command_line (); Gtk.main (); return 0;new file mode 100644@@ -0,0 +1,35 @@+/*+ Copyright (C) 2016 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library 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 + Lesser General Public License for more details.+*/++using Gee;+using SvgBird;+using B;++namespace Bird {++class AbsoluteLayout : Component {++ public AbsoluteLayout () {+ }++ public AbsoluteLayout.for_tag (XmlElement layout_tag) {+ parse (layout_tag);+ }++ public override string to_string () {+ return "AbsoluteLayout";+ }+}++}new file mode 100644@@ -0,0 +1,94 @@+/*+ Copyright (C) 2016 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library 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 + Lesser General Public License for more details.+*/++using Gee;+using SvgBird;+using B;++namespace Bird {++public enum BoxOrientation {+ HORIZONTAL,+ VERTICAL+}++class BoxLayout : Component {++ public BoxOrientation orientation { get; private set; }+ + public BoxLayout (BoxOrientation orientation) {+ base ();+ this.orientation = orientation;+ }++ public BoxLayout.for_tag (XmlElement layout_tag, BoxOrientation orientation) {+ base.for_tag (layout_tag);+ this.orientation = orientation;+ }+ + public override void layout () {+ switch (orientation) {+ case BoxOrientation.HORIZONTAL:+ layout_horizontal ();+ break;+ case BoxOrientation.VERTICAL:+ layout_vertical ();+ break;+ }+ }++ void layout_horizontal () {+ double child_x = 0;+ double child_y = 0;+ + foreach (Component component in components) {+ component.x = child_x;+ component.y = child_y;+ component.layout ();+ child_x += component.padded_width;+ + if (component.height > height) {+ height = component.height;+ }+ }+ + width = child_x;+ }++ void layout_vertical () {+ double child_x = 0;+ double child_y = 0;+ + foreach (Component component in components) {+ component.x = child_x;+ component.y = child_y;+ component.layout ();+ component.apply_padding ();++ child_y += component.padded_height;+ + if (component.width > width) {+ width = component.width;+ }+ }+ + height = child_y;+ }++ public override string to_string () {+ return "BoxLayout";+ }+}++}new file mode 100644@@ -0,0 +1,291 @@+/*+ Copyright (C) 2016 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library 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 + Lesser General Public License for more details.+*/++using B;+using SvgBird;+using Gee;+using Cairo;++namespace Bird {++class Component : GLib.Object {+ public double width { get; protected set; }+ public double height { get; protected set; }+ + public double padded_width {+ get {+ return width + get_padding_top () + get_padding_bottom ();+ }+ }+ + public double padded_height { + get {+ return height + get_padding_left () + get_padding_right ();+ }+ }+ + /** Vertical placement for this component relative to the parent container. */+ public double x { get; protected set; }+ + /** Horizontal placement for this component relative to the parent container. */+ public double y { get; protected set; }++ /** The parts this component is made of. */+ protected ArrayList<Component> components = new ArrayList<Component> ();++ XmlElement? component_tag = null;+ + /** Style sheet and other SVG definitions. */+ Defs? defs = null;+ SvgStyle style = new SvgStyle ();++ string? css_class = null;+ string? id = null;++ public Component () {+ }++ public Component.for_tag (XmlElement component_tag) {+ this.component_tag = component_tag;+ parse (component_tag);+ }++ private void inherit_style (Defs? defs) {+ this.defs = defs;+ }++ protected void apply_padding () { + }++ protected void load_svg_file (string file_name) {+ SvgComponent svg = new SvgComponent.for_file (file_name);+ add_component (svg); + }++ protected void parse_style (XmlElement style_tag) {+ Defs definitions = new Defs (); + StyleSheet style_sheet = StyleSheet.parse (definitions, style_tag);+ + if (defs != null) {+ Defs subscope_definitions = ((!) defs).shallow_copy ();+ subscope_definitions.style_sheet.merge (style_sheet);+ } else {+ defs = definitions;+ }+ }+ + protected void parse_svg (XmlElement svg_tag) {+ foreach (Attribute attribute in svg_tag.get_attributes ()) {+ string attribute_name = attribute.get_name ();+ + if (attribute_name == "file") {+ load_svg_file (attribute.get_content ());+ }+ }+ }++ protected void add_component (Component component) {+ components.add (component);+ component.inherit_style (defs);+ + if (component.component_tag != null) {+ style = SvgStyle.parse (defs, style, (!) component.component_tag);+ }+ }++ protected void parse_layout (XmlElement layout_tag) {+ foreach (Attribute attribute in layout_tag.get_attributes ()) {+ string attribute_name = attribute.get_name ();+ + if (attribute_name == "type") {+ if (attribute.get_content () == "hbox") {+ HBox hbox = new HBox.for_tag (layout_tag);+ add_component (hbox);+ } else if (attribute.get_content () == "vbox") {+ VBox hbox = new VBox.for_tag (layout_tag);+ add_component (hbox);+ } else {+ warning ("Layout of type " + attribute.get_content ()+ + " is not implemented in this verison.");+ }+ } else if (attribute_name == "id") {+ id = attribute.get_content ();+ } else if (attribute_name == "class") {+ css_class = attribute.get_content ();+ } else if (attribute_name == "style") {+ // style will be parsed later+ } else {+ unused_attribute (attribute_name);+ }+ }+ }++ protected void parse_component (XmlElement component_tag) {+ foreach (Attribute attribute in component_tag.get_attributes ()) {+ string attribute_name = attribute.get_name ();+ + if (attribute_name == "file") {+ Component component = new Component ();+ component.load (attribute.get_content ());+ add_component (component);+ } else {+ unused_attribute (attribute_name);+ }+ }+ }+ + protected void parse (XmlElement component_tag) {+ foreach (XmlElement tag in component_tag) {+ string tag_name = tag.get_name ();+ + if (tag_name == "ui") {+ parse (tag);+ } else if (tag_name == "layout") {+ parse_layout (tag);+ } else if (tag_name == "component") {+ parse_component (tag);+ } else if (tag_name == "svg") {+ parse_svg (tag);+ } else if (tag_name == "style") {+ parse_style (tag);+ } else {+ unused_tag (tag_name);+ }+ }+ }++ internal void unused_attribute (string attribute) {+ warning ("The attribute " + attribute + " is not known in this version.");+ }+ + internal void unused_tag (string tag_name) {+ warning ("The tag " + tag_name + " is not known in this version.");+ }++ public void load (string file_name) {+ if (file_name.has_suffix (".ui")) {+ load_layout (file_name);+ } else if (file_name.has_suffix (".svg")) {+ load_svg_file (file_name);+ } else {+ warning (file_name + " is not a ui file or svg file.");+ }+ }++ public void load_layout (string file_name) {+ string? path = find_file (file_name);+ + if (path == null) {+ warning (file_name + " not found.");+ return;+ }+ + string xml_data;+ File layout_file = File.new_for_path ((!) path); + + try {+ FileUtils.get_contents((!) layout_file.get_path (), out xml_data);+ } catch (GLib.Error error) {+ warning (error.message);+ return;+ }++ XmlTree xml_parser = new XmlTree (xml_data);+ Component component = new Component.for_tag (xml_parser.get_root ());+ add_component (component);+ layout ();+ }+ + public static string find_file (string file_name) {+ File file = File.new_for_path ("birdui/" + file_name);+ + if (file.query_exists ()) {+ return (!) file.get_path ();+ }+ + return file_name;+ }+ + public virtual void layout () {+ if (unlikely (components.size > 1)) {+ warning ("A component has several parts but no layout has been set.");+ }+ + foreach (Component component in components) {+ component.layout ();+ width = component.width;+ height = component.height;+ }+ }+ + public virtual void draw (Context cairo) {+ foreach (Component component in components) {+ cairo.save ();+ cairo.translate (component.x, component.y);+ component.draw (cairo);+ cairo.restore ();+ }+ }+ + public virtual void motion_notify_event (double x, double y) { + }+ + public virtual void button_press_event (uint button, double x, double y) { + }+ + public virtual string to_string () {+ if (component_tag != null) {+ XmlElement tag = (!) component_tag;+ return @"Component $(tag.get_name ())";+ } else {+ + return "Component";+ }+ }+ + public void print_tree () {+ print_tree_level (0);+ }++ protected void print_tree_level (int indent) {+ for (int i = 0; i < indent; i++) {+ print ("\t");+ }+ + print (@"$(to_string ()), x: $x, y: $y, w: $width, h: $height\n");+ + foreach (Component component in components) {+ component.print_tree_level (indent + 1);+ }+ }+ + double get_padding_bottom () {+ return SvgFile.parse_number (style.get_css_property ("padding-bottom"));+ }++ double get_padding_top () {+ return SvgFile.parse_number (style.get_css_property ("padding-top"));+ }++ double get_padding_left () {+ return SvgFile.parse_number (style.get_css_property ("padding-left"));+ }++ double get_padding_right () {+ return SvgFile.parse_number (style.get_css_property ("padding-right"));+ }+}++}+new file mode 100644@@ -0,0 +1,51 @@+/*+ Copyright (C) 2016 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library 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 + Lesser General Public License for more details.+*/++using Gtk;+using Gdk;+using Cairo;++namespace Bird {++class Widget : DrawingArea {+ + Component component;+ + public Widget (Component main_component) { + component = main_component;++ add_events (EventMask.BUTTON_PRESS_MASK + | EventMask.POINTER_MOTION_MASK+ | EventMask.LEAVE_NOTIFY_MASK);+ + motion_notify_event.connect ((event)=> {+ component.motion_notify_event (event.x, event.y);+ return true;+ }); + + button_press_event.connect ((event)=> {+ component.button_press_event (event.button, event.x, event.y);+ return true;+ });++ draw.connect ((event) => {+ Context cairo_context = cairo_create (get_window ());+ component.draw (cairo_context);+ return true;+ });+ }+ +}++}new file mode 100644@@ -0,0 +1,38 @@+/*+ Copyright (C) 2016 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library 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 + Lesser General Public License for more details.+*/++using Bird;+using Gtk;++int main (string[] args) {+ Gtk.init (ref args);+ + Window window = new Window ();+ window.set_title ("UI Bird");+ window.destroy.connect (Gtk.main_quit);+ + Component layout = new Component ();+ layout.load ("test.ui");+ + Bird.Widget primary_layout = new Bird.Widget (layout);++ Box vbox = new Box (Orientation.VERTICAL, 0);+ vbox.pack_start(primary_layout, true, true, 0);+ window.add (vbox);+ + window.show_all ();+ + Gtk.main ();+ return 0;+}new file mode 100644@@ -0,0 +1,34 @@+/*+ Copyright (C) 2016 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library 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 + Lesser General Public License for more details.+*/++using Gee;+using B;++namespace Bird {++class HBox : BoxLayout {+ public HBox () {+ base (BoxOrientation.HORIZONTAL);+ }++ public HBox.for_tag (XmlElement layout) {+ base.for_tag (layout, BoxOrientation.HORIZONTAL);+ }+ + public override string to_string () {+ return "HBox";+ }+}++}new file mode 100644@@ -0,0 +1,34 @@+/*+ Copyright (C) 2016 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library 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 + Lesser General Public License for more details.+*/++using B;+using SvgBird;+using Gee;+using Cairo;++namespace Bird {++public class Style : GLib.Object {+ public SvgStyle svg_style;+ + public Style () {+ svg_style = new SvgStyle ();+ }+ + public Style.for_svg (SvgStyle svg_style) {+ this.svg_style = svg_style;+ }+}++}new file mode 100644@@ -0,0 +1,88 @@+/*+ Copyright (C) 2016 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library 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 + Lesser General Public License for more details.+*/++using B;+using SvgBird;+using Gee;+using Cairo;++namespace Bird {++class SvgComponent : Component {+ string? path = null;+ string file_name = "";+ SvgDrawing? drawing = null;+ + public SvgComponent () {+ base ();+ }++ public SvgComponent.for_tag (XmlElement svg_component_tag) {+ base ();+ }++ public SvgComponent.for_file (string svg_file) {+ base ();+ load_svg (svg_file);+ }++ public override void layout () {+ if (unlikely (components.size > 0)) {+ warning ("SVG files can not have subviews.");+ }+ + if (drawing != null) {+ SvgDrawing svg = (!) drawing;+ width = svg.width;+ height = svg.height;+ }+ }+ + public override string to_string () {+ return "Svg: " + file_name;+ }++ private void load_svg (string file_name) {+ this.file_name = file_name;+ path = find_file (file_name);+ + if (path == null) {+ warning (file_name + " not found.");+ return;+ }+ + string xml_data;+ File svg_file = File.new_for_path ((!) path); + try {+ FileUtils.get_contents((!) svg_file.get_path (), out xml_data);+ } catch (GLib.Error error) {+ warning (error.message);+ return;+ }+ + SvgFile svg_parser = new SvgFile ();+ drawing = svg_parser.parse_svg_data (xml_data);+ }++ public override void draw (Context cairo) {+ if (drawing != null) {+ SvgDrawing svg = (!) drawing;+ svg.draw (cairo);+ }+ }++}++}+new file mode 100644@@ -0,0 +1,35 @@+/*+ Copyright (C) 2016 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library 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 + Lesser General Public License for more details.+*/++using Gee;+using B;++namespace Bird {+ +class VBox : BoxLayout {+ public VBox () {+ base (BoxOrientation.VERTICAL);+ }++ public VBox.for_tag (XmlElement layout) {+ base.for_tag (layout, BoxOrientation.VERTICAL);+ }+ + public override string to_string () {+ return "VBox";+ }++}++}@@ -559,4 +559,55 @@ def make_birdfont_test(target_binary, deps): def task_birdfont_test(): yield make_birdfont_test('birdfont-test', ['libbirdgems.so', 'libbirdfont.so']) +def make_birdui(target_binary, deps):+ valac_command = config.VALAC + """\+ -C \+ --pkg posix \+ --pkg """ + config.GEE + """ \+ --pkg gtk+-3.0 \+ --pkg libsvgbird \+ --pkg xmlbird \+ --vapidir=./ \+ --basedir=build/birdui/ \+ """ + config.NON_NULL + """ \+ --enable-experimental \+ birdui/*.vala \+ """++ cc_command = config.CC + """ \+ -fPIC \+ $(pkg-config --cflags gtk+-3.0) \+ $(pkg-config --cflags glib-2.0) \+ $(pkg-config --cflags xmlbird) \+ $(pkg-config --cflags """ + config.GEE + """) \+ -g \+ -I ./build/libsvgbird \+ -c C_SOURCE \+ -o OBJECT_FILE \+ """++ linker_command = config.CC + """ \+ """ + soname(target_binary) + """ \+ -fPIC \+ -g \+ build/birdui/*.o \+ $(pkg-config --libs gtk+-3.0) \+ $(pkg-config --libs glib-2.0) \+ $(pkg-config --libs gobject-2.0) \+ $(pkg-config --libs xmlbird) \+ $(pkg-config --libs """ + config.GEE + """) \+ -L ./build/bin -l svgbird \+ -o build/bin/""" + target_binary++ libbirdgems = Builder('birdui',+ valac_command, + cc_command,+ linker_command,+ target_binary,+ None,+ deps)+ + yield libbirdgems.build() +def task_birdui():+ yield make_birdui('birdui', []) @@ -29,6 +29,14 @@ public class AttributePattern : GLib.Object { public string name = ""; public string? content = null; public AttributePatternType type = AttributePatternType.NONE;++ public AttributePattern copy () {+ AttributePattern a = new AttributePattern ();+ a.name = name;+ a.content = content;+ a.type = type;+ return a;+ } public bool match (Attributes attributes) { switch (type) {@@ -95,9 +95,9 @@ public class Color { public string to_rgb_hex () { StringBuilder rgb = new StringBuilder (); rgb.append ("#");- rgb.append_printf ("%x", (int) Math.rint (r * 254));- rgb.append_printf ("%x", (int) Math.rint (g * 254));- rgb.append_printf ("%x", (int) Math.rint (b * 254));+ rgb.append_printf ("%02x", (int) Math.rint (r * 254));+ rgb.append_printf ("%02x", (int) Math.rint (g * 254));+ rgb.append_printf ("%02x", (int) Math.rint (b * 254)); return rgb.str; } @@ -108,7 +108,6 @@ public class Color { return rgba.str; } - public Color copy () { return new Color (r, g, b, a); }@@ -81,16 +81,29 @@ public class Defs { return ((!) attribute).has_prefix ("url"); } - public Defs copy () {+ public Defs shallow_copy () { Defs d = new Defs (); foreach (Gradient g in gradients) { d.add (g); } + d.style_sheet = style_sheet.shallow_copy ();+ return d; } + public Defs copy () {+ Defs d = new Defs ();+ + foreach (Gradient g in gradients) {+ d.add (g.copy ());+ }+ + d.style_sheet = style_sheet.copy ();+ + return d;+ } } }@@ -91,7 +91,7 @@ public abstract class Object : GLib.Object { bool need_stroke = style.stroke_gradient != null || style.stroke != null; cr.set_line_width (style.stroke_width);- + if (style.fill_gradient != null) { apply_gradient (cr, (!) style.fill_gradient); } else if (style.fill != null) {@@ -21,7 +21,12 @@ public class Points : GLib.Object { public double x = 0; public double y = 0; public bool closed = false;-+ public int size {+ get {+ return point_data.size;+ }+ }+ public void add (double p) { point_data.add (p); }@@ -19,11 +19,10 @@ namespace SvgBird { public class Selector : GLib.Object { - Gee.ArrayList<SelectorPattern> patterns;+ Gee.ArrayList<SelectorPattern> patterns = new Gee.ArrayList<SelectorPattern> (); public SvgStyle style { get; set; } public Selector (string pattern, SvgStyle style) {- this.patterns = new Gee.ArrayList<SelectorPattern> (); string[] selector_patterns = pattern.split (","); for (int i = 0; i < selector_patterns.length; i++) {@@ -33,6 +32,18 @@ public class Selector : GLib.Object { this.style = style; } + public Selector.copy_constructor (Selector selector) {+ style = selector.style.copy ();+ + foreach (SelectorPattern pattern in selector.patterns) {+ selector.patterns.add (pattern.copy ());+ }+ }+ + public Selector copy () {+ return new Selector.copy_constructor (this);+ }+ public bool match (XmlElement tag, string? id, string? css_class) { foreach (SelectorPattern pattern in patterns) { if (pattern.match (tag, id, css_class)) {@@ -19,13 +19,15 @@ namespace SvgBird { /** A CSS selector pattern. */ public class SelectorPattern : GLib.Object {- Gee.ArrayList<SelectorTag> tags;+ Gee.ArrayList<SelectorTag> tags = new Gee.ArrayList<SelectorTag> (); + public SelectorPattern.empty () {+ }+ public SelectorPattern (string pattern) { string p = pattern.strip (); string[] elements = p.split (" ");- tags = new Gee.ArrayList<SelectorTag> ();- + foreach (string element in elements) { if (element != "") { tags.add (new SelectorTag (element));@@ -33,12 +35,23 @@ public class SelectorPattern : GLib.Object { } } + public SelectorPattern copy () {+ SelectorPattern pattern = new SelectorPattern.empty ();+ + foreach (SelectorTag tag in tags) {+ pattern.tags.add (tag.copy ());+ }+ + return pattern;+ }+ public bool match (XmlElement tag, string? id, string? css_class) { for (int i = tags.size - 1; i >= 0; i--) { SelectorTag pattern = tags.get (i); if (pattern.name == ">") { if (i - 1 < 0) {+ return false; } @@ -24,6 +24,9 @@ public class SelectorTag : GLib.Object { string? css_class = null; Gee.ArrayList<AttributePattern>? attribute_patterns = null; + public SelectorTag.empty () {+ }+ public SelectorTag (string pattern) { string tag_pattern = pattern.strip (); int id_separator = tag_pattern.index_of ("#");@@ -61,6 +64,22 @@ public class SelectorTag : GLib.Object { } } + public SelectorTag copy () {+ SelectorTag tag = new SelectorTag.empty();+ + tag.name = name;+ tag.id = id;+ tag.css_class = css_class;+ + if (attribute_patterns != null) { + foreach (AttributePattern p in (!) attribute_patterns) {+ ((!) attribute_patterns).add (p.copy ());+ }+ }+ + return tag;+ }+ void parse_attributes (string attributes) { int index = 0; Gee.ArrayList<AttributePattern> patterns = new Gee.ArrayList<AttributePattern> ();@@ -139,7 +158,6 @@ public class SelectorTag : GLib.Object { } if (((!) this.css_class) != ((!) css_class)) {- print(@"class \"$((!)this.css_class)\"=\"$((!)css_class)\"\n"); return false; } }@@ -17,6 +17,7 @@ using Math; namespace SvgBird { + public class StyleSheet : GLib.Object { public Gee.ArrayList<Selector> styles;@@ -25,6 +26,27 @@ public class StyleSheet : GLib.Object { styles = new Gee.ArrayList<Selector> (); } + /** Copy references to all selectors in this style sheet. */+ public StyleSheet shallow_copy () {+ StyleSheet style_sheet = new StyleSheet ();+ + foreach (Selector selector in styles) {+ style_sheet.styles.add (selector);+ }+ + return style_sheet;+ }++ public StyleSheet copy () {+ StyleSheet style_sheet = new StyleSheet ();+ + foreach (Selector selector in styles) {+ style_sheet.styles.add (selector.copy ());+ }+ + return style_sheet;+ }+ public void inherit_style (XmlElement tag, SvgStyle style) { string? id = null; string? css_class = null;@@ -46,6 +68,12 @@ public class StyleSheet : GLib.Object { } } + public void merge (StyleSheet style_sheet) {+ foreach (Selector selector in style_sheet.styles) {+ styles.add (selector);+ }+ }+ public static StyleSheet parse (Defs defs, XmlElement style_tag) { StyleSheet style_sheet = new StyleSheet (); string css = style_tag.get_content ();@@ -138,9 +138,6 @@ public static void get_arc_arguments (double x0, double y0, } else if (sweepFlag && angleExtent < 0) { angleExtent += 2 * PI; }- - //center_x = cx - rx;- //center_y = cy - ry; center_x = cx; center_y = cy;@@ -150,8 +147,6 @@ public static void get_arc_arguments (double x0, double y0, angle_start = angleStart; angle_extent = angleExtent;-- print (@"angleStart: $angle_start angleExtent $angle_extent\n"); } @@ -24,6 +24,11 @@ public class SvgFile : GLib.Object { public SvgFile () { }++ public SvgDrawing parse_svg_data (string xml_data) {+ XmlTree tree = new XmlTree (xml_data);+ return parse_svg_file (tree.get_root ());+ } public SvgDrawing parse_svg_file (XmlElement svg_tag) { drawing = new SvgDrawing ();@@ -43,7 +48,7 @@ public class SvgFile : GLib.Object { foreach (XmlElement t in svg_tag) { string name = t.get_name ();- + if (name == "g") { parse_layer (drawing.root_layer, style, t); }@@ -642,8 +647,19 @@ public class SvgFile : GLib.Object { for (int i = 0; i < points_size; i++) { // FIXME: add more types if (bezier_points[i].type == 'M') {- points.x = bezier_points[i].x0;- points.y = bezier_points[i].y0;+ if (i == 0) {+ points.x = bezier_points[i].x0;+ points.y = bezier_points[i].y0;+ } else {+ points.add_type (LINE);+ points.add (bezier_points[i].x0);+ points.add (bezier_points[i].y0);+ points.add (0);+ points.add (0);+ points.add (0);+ points.add (0);+ points.add (0);+ } } else if (bezier_points[i].type == 'C') { points.add_type (CUBIC); points.add (bezier_points[i].x0);@@ -694,10 +710,10 @@ public class SvgFile : GLib.Object { } } - if (points.point_data.size > 0) {+ if (points.size > 0) { path_data.add (points); }-+ return path_data; } @@ -70,7 +70,6 @@ public class SvgPath : Object { case LINE: cr.line_to (points[i + 1].value, points[i + 2].value); break;- } } }@@ -36,6 +36,16 @@ public class SvgStyle : GLib.Object { set_style_properties (defs, this); } + public string? get_css_property (string key) {+ return style.get (key);+ }++ public SvgStyle copy () {+ SvgStyle style = new SvgStyle ();+ style.inherit (this);+ return style;+ }+ public LineCap get_line_cap () { string l; @@ -169,10 +179,34 @@ public class SvgStyle : GLib.Object { k = pair[0].strip (); v = pair[1].strip (); - style.set (k, v);+ if (k == "padding") {+ parse_padding_shorthand (v);+ } else {+ style.set (k, v);+ } } } }+ + void parse_padding_shorthand (string arguments) {+ string[] args = StyleSheet.replace_whitespace (arguments).split (" ");+ + if (args.length > 0) {+ style.set ("padding-top", args[0]);+ }+ + if (args.length > 1) {+ style.set ("padding-right", args[1]);+ }++ if (args.length > 2) {+ style.set ("padding-bottom", args[2]);+ }++ if (args.length > 3) {+ style.set ("padding-left", args[3]);+ }+ } } }