| // Copyright 2007 The Closure Library Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS-IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| |
| /** |
| * @fileoverview A thicker wrapper around graphics groups. |
| * @author robbyw@google.com (Robby Walker) |
| */ |
| |
| |
| goog.provide('goog.graphics.ext.Group'); |
| |
| goog.require('goog.array'); |
| goog.require('goog.graphics.ext.Element'); |
| |
| |
| |
| /** |
| * Wrapper for a graphics group. |
| * @param {goog.graphics.ext.Group} group Parent for this element. Can |
| * be null if this is a Graphics instance. |
| * @param {goog.graphics.GroupElement=} opt_wrapper The thin wrapper |
| * to wrap. If omitted, a new group will be created. Must be included |
| * when group is null. |
| * @constructor |
| * @extends {goog.graphics.ext.Element} |
| */ |
| goog.graphics.ext.Group = function(group, opt_wrapper) { |
| opt_wrapper = opt_wrapper || group.getGraphicsImplementation().createGroup( |
| group.getWrapper()); |
| goog.graphics.ext.Element.call(this, group, opt_wrapper); |
| |
| /** |
| * Array of child elements this group contains. |
| * @type {Array<goog.graphics.ext.Element>} |
| * @private |
| */ |
| this.children_ = []; |
| }; |
| goog.inherits(goog.graphics.ext.Group, goog.graphics.ext.Element); |
| |
| |
| /** |
| * Add an element to the group. This should be treated as package local, as |
| * it is called by the draw* methods. |
| * @param {!goog.graphics.ext.Element} element The element to add. |
| * @param {boolean=} opt_chain Whether this addition is part of a longer set |
| * of element additions. |
| */ |
| goog.graphics.ext.Group.prototype.addChild = function(element, opt_chain) { |
| if (!goog.array.contains(this.children_, element)) { |
| this.children_.push(element); |
| } |
| |
| var transformed = this.growToFit_(element); |
| |
| if (element.isParentDependent()) { |
| element.parentTransform(); |
| } |
| |
| if (!opt_chain && element.isPendingTransform()) { |
| element.reset(); |
| } |
| |
| if (transformed) { |
| this.reset(); |
| } |
| }; |
| |
| |
| /** |
| * Remove an element from the group. |
| * @param {goog.graphics.ext.Element} element The element to remove. |
| */ |
| goog.graphics.ext.Group.prototype.removeChild = function(element) { |
| goog.array.remove(this.children_, element); |
| |
| // TODO(robbyw): shape.fireEvent('delete') |
| |
| this.getGraphicsImplementation().removeElement(element.getWrapper()); |
| }; |
| |
| |
| /** |
| * Calls the given function on each of this component's children in order. If |
| * {@code opt_obj} is provided, it will be used as the 'this' object in the |
| * function when called. The function should take two arguments: the child |
| * component and its 0-based index. The return value is ignored. |
| * @param {Function} f The function to call for every child component; should |
| * take 2 arguments (the child and its index). |
| * @param {Object=} opt_obj Used as the 'this' object in f when called. |
| */ |
| goog.graphics.ext.Group.prototype.forEachChild = function(f, opt_obj) { |
| if (this.children_) { |
| goog.array.forEach(this.children_, f, opt_obj); |
| } |
| }; |
| |
| |
| /** |
| * @return {goog.graphics.GroupElement} The underlying thin wrapper. |
| * @override |
| */ |
| goog.graphics.ext.Group.prototype.getWrapper; |
| |
| |
| /** |
| * Reset the element. |
| * @override |
| */ |
| goog.graphics.ext.Group.prototype.reset = function() { |
| goog.graphics.ext.Group.superClass_.reset.call(this); |
| |
| this.updateChildren(); |
| }; |
| |
| |
| /** |
| * Called from the parent class, this method resets any pre-computed positions |
| * and sizes. |
| * @protected |
| * @override |
| */ |
| goog.graphics.ext.Group.prototype.redraw = function() { |
| this.getWrapper().setSize(this.getWidth(), this.getHeight()); |
| this.transformChildren(); |
| }; |
| |
| |
| /** |
| * Transform the children that need to be transformed. |
| * @protected |
| */ |
| goog.graphics.ext.Group.prototype.transformChildren = function() { |
| this.forEachChild(function(child) { |
| if (child.isParentDependent()) { |
| child.parentTransform(); |
| } |
| }); |
| }; |
| |
| |
| /** |
| * As part of the reset process, update child elements. |
| */ |
| goog.graphics.ext.Group.prototype.updateChildren = function() { |
| this.forEachChild(function(child) { |
| if (child.isParentDependent() || child.isPendingTransform()) { |
| child.reset(); |
| } else if (child.updateChildren) { |
| child.updateChildren(); |
| } |
| }); |
| }; |
| |
| |
| /** |
| * When adding an element, grow this group's bounds to fit it. |
| * @param {!goog.graphics.ext.Element} element The added element. |
| * @return {boolean} Whether the size of this group changed. |
| * @private |
| */ |
| goog.graphics.ext.Group.prototype.growToFit_ = function(element) { |
| var transformed = false; |
| |
| var x = element.getMaxX(); |
| if (x > this.getWidth()) { |
| this.setMinWidth(x); |
| transformed = true; |
| } |
| |
| var y = element.getMaxY(); |
| if (y > this.getHeight()) { |
| this.setMinHeight(y); |
| transformed = true; |
| } |
| |
| return transformed; |
| }; |
| |
| |
| /** |
| * @return {number} The width of the element's coordinate space. |
| */ |
| goog.graphics.ext.Group.prototype.getCoordinateWidth = function() { |
| return this.getWidth(); |
| }; |
| |
| |
| /** |
| * @return {number} The height of the element's coordinate space. |
| */ |
| goog.graphics.ext.Group.prototype.getCoordinateHeight = function() { |
| return this.getHeight(); |
| }; |
| |
| |
| /** |
| * Remove all drawing elements from the group. |
| */ |
| goog.graphics.ext.Group.prototype.clear = function() { |
| while (this.children_.length) { |
| this.removeChild(this.children_[0]); |
| } |
| }; |