Thanks, John! This would be a nice contribution to core qooxdoo.
D
On Tue, Jan 12, 2016 at 11:46 AM John Spackman <john-li...@zenesis.com>
wrote:
> No not really, I think it probably should go across just fine but OTOH I
> first wrote it several years ago (e.g. Qx v3.x or maybe even earlier) so
> there could be things that were added to Qx that mine doesn’t have. I’ll
> have a go at comparing them side by side to give you a better answer!
>
> John.
>
> From: Derrell Lipman <derrell.lip...@unwireduniverse.com>
> Reply-To: qooxdoo Development <qooxdoo-devel@lists.sourceforge.net>
> Date: Tuesday, 12 January 2016 at 13:25
> To: qooxdoo Development <qooxdoo-devel@lists.sourceforge.net>
> Subject: Re: [qooxdoo-devel] Scaling (but not skewing) Atom image
>
> Thanks, John! Do you know if there anything about your Image class that
> would prevent it from directly replacing the native qooxdoo Image class?
> Any known non-backward-compatibilities? As I searched the archives, I found
> that there have been many questions about how to do this over the years...
>
> Derrell
>
>
> On Tue, Jan 12, 2016 at 8:09 AM John Spackman <john-li...@zenesis.com>
> wrote:
>
>> The only solution I have for this is to reimplement qx.core.basic.Image,
>> but with added tweaks for scaling. I’ve created a playground demo but it
>> is too large for a shortened URL because out is basically my entire Image
>> class plus the demo code, so here it is in email:
>>
>>
>> /**
>>
>> * Image which preserves the aspect ratio while scaling the image and
>> constrains
>>
>> * the dimensions to stay within the min/max width/height. The image is
>> placed
>>
>> * centrally within the dimensions of the widget.
>>
>> *
>>
>> * Based on the Qooxdoo image
>>
>> */
>>
>> qx.Class.define("grasshopper.af.ui.image.Image", {
>>
>> extend : qx.ui.core.Widget,
>>
>>
>> /*
>>
>> *
>> ****************************************************************************
>>
>> * CONSTRUCTOR
>>
>> *
>> ****************************************************************************
>>
>> */
>>
>>
>> /**
>>
>> * @param source
>>
>> * {String?null} The URL of the image to display.
>>
>> */
>>
>> construct : function(source) {
>>
>> this.__contentElements = {};
>>
>>
>> this.base(arguments);
>>
>>
>> if (source) {
>>
>> this.setSource(source);
>>
>> }
>>
>> },
>>
>>
>> /*
>>
>> *
>> ****************************************************************************
>>
>> * PROPERTIES
>>
>> *
>> ****************************************************************************
>>
>> */
>>
>>
>> properties : {
>>
>> /** The URL of the image */
>>
>> source : {
>>
>> check : "String",
>>
>> init : null,
>>
>> nullable : true,
>>
>> event : "changeSource",
>>
>> apply : "_applySource",
>>
>> themeable : true
>>
>> },
>>
>>
>>
>> /**
>>
>> * Whether the image should be scaled to the given dimensions
>>
>> *
>>
>> * This is disabled by default because it prevents the usage of image
>>
>> * clipping when enabled.
>>
>> */
>>
>> scale : {
>>
>> check : "Boolean",
>>
>> init : true,
>>
>> themeable : true,
>>
>> apply : "_applyScale"
>>
>> },
>>
>>
>> /**
>>
>> * Whether to preserve the image ratio (ie prevent distortion), and
>> which dimension
>>
>> * to prioritise
>>
>> */
>>
>> forceRatio : {
>>
>> init : 'auto',
>>
>> check : [ 'disabled', 'height', 'width', 'auto' ],
>>
>> apply : '_applyDimension'
>>
>> },
>>
>>
>> /**
>>
>> * Whether to allow scaling the image up
>>
>> */
>>
>> allowScaleUp : {
>>
>> init : false,
>>
>> check : "Boolean",
>>
>> apply : "_applyDimension"
>>
>> },
>>
>>
>> // overridden
>>
>> appearance : {
>>
>> refine : true,
>>
>> init : "image"
>>
>> },
>>
>>
>> // overridden
>>
>> allowShrinkX : {
>>
>> refine : true,
>>
>> init : false
>>
>> },
>>
>>
>> // overridden
>>
>> allowShrinkY : {
>>
>> refine : true,
>>
>> init : false
>>
>> },
>>
>>
>> // overridden
>>
>> allowGrowX : {
>>
>> refine : true,
>>
>> init : false
>>
>> },
>>
>>
>> // overridden
>>
>> allowGrowY : {
>>
>> refine : true,
>>
>> init : false
>>
>> }
>>
>> },
>>
>>
>> /*
>>
>> *
>> ****************************************************************************
>>
>> * EVENTS
>>
>> *
>> ****************************************************************************
>>
>> */
>>
>>
>> events : {
>>
>> /**
>>
>> * Fired if the image source can not be loaded.
>>
>> *
>>
>> * *Attention*: This event is only used for images which are loaded
>>
>> * externally (aka unmanaged images).
>>
>> */
>>
>> loadingFailed : "qx.event.type.Event",
>>
>>
>> /**
>>
>> * Fired if the image has been loaded.
>>
>> *
>>
>> * *Attention*: This event is only used for images which are loaded
>>
>> * externally (aka unmanaged images).
>>
>> */
>>
>> loaded : "qx.event.type.Event"
>>
>> },
>>
>>
>> /*
>>
>> *
>> ****************************************************************************
>>
>> * MEMBERS
>>
>> *
>> ****************************************************************************
>>
>> */
>>
>>
>> members : {
>>
>> __width : null,
>>
>> __height : null,
>>
>> __mode : null,
>>
>> __contentElements : null,
>>
>> __currentContentElement : null,
>>
>> __wrapper : null,
>>
>>
>> // overridden
>>
>> _onChangeTheme : function() {
>>
>> this.base(arguments);
>>
>> // restyle source (theme change might have changed the resolved url
>> )
>>
>> this._styleSource();
>>
>> },
>>
>>
>>
>> /*
>>
>> *
>> ---------------------------------------------------------------------------
>>
>> * WIDGET API
>>
>> *
>> ---------------------------------------------------------------------------
>>
>> */
>>
>>
>> // overridden
>>
>> getContentElement : function() {
>>
>> return this.__getSuitableContentElement();
>>
>> },
>>
>>
>> // overridden
>>
>> _createContentElement : function() {
>>
>> return this.__getSuitableContentElement();
>>
>> },
>>
>>
>> // overridden
>>
>> _getContentHint : function() {
>>
>> return {
>>
>> width : this.__width || 0,
>>
>> height : this.__height || 0
>>
>> };
>>
>> },
>>
>>
>>
>> _applyDimension: function(value, oldValue) {
>>
>> this.base(arguments, value, oldValue);
>>
>> this.__updateContentHint();
>>
>> },
>>
>>
>> // overridden
>>
>> _applyDecorator : function(value, old) {
>>
>> this.base(arguments, value, old);
>>
>>
>> var source = this.getSource();
>>
>> source = qx.util.AliasManager.getInstance().resolve(source);
>>
>> var el = this.getContentElement();
>>
>> if (this.__wrapper) {
>>
>> el = el.getChild(0);
>>
>> }
>>
>> this.__setSource(el, source);
>>
>> },
>>
>>
>> // overridden
>>
>> _applyPadding : function(value, old, name) {
>>
>> this.base(arguments, value, old, name);
>>
>>
>> var element = this.getContentElement();
>>
>> if (this.__wrapper) {
>>
>> element.getChild(0).setStyles({
>>
>> top : this.getPaddingTop() || 0,
>>
>> left : this.getPaddingLeft() || 0
>>
>> });
>>
>> } else {
>>
>> element.setPadding(this.getPaddingLeft() || 0, this.getPaddingTop()
>> || 0);
>>
>> }
>>
>>
>> },
>>
>>
>> renderLayout : function(left, top, width, height) {
>>
>> this.base(arguments, left, top, width, height);
>>
>>
>> var element = this.getContentElement();
>>
>> if (this.__wrapper) {
>>
>> element.getChild(0).setStyles({
>>
>> width : width - (this.getPaddingLeft() || 0) -
>> (this.getPaddingRight()
>> || 0),
>>
>> height : height - (this.getPaddingTop() || 0) -
>> (this.getPaddingBottom()
>> || 0),
>>
>> top : this.getPaddingTop() || 0,
>>
>> left : this.getPaddingLeft() || 0
>>
>> });
>>
>> }
>>
>> },
>>
>>
>> /*
>>
>> *
>> ---------------------------------------------------------------------------
>>
>> * IMAGE API
>>
>> *
>> ---------------------------------------------------------------------------
>>
>> */
>>
>>
>> // property apply, overridden
>>
>> _applyEnabled : function(value, old) {
>>
>> this.base(arguments, value, old);
>>
>>
>> if (this.getSource()) {
>>
>> this._styleSource();
>>
>> }
>>
>> },
>>
>>
>> // property apply
>>
>> _applySource : function(value) {
>>
>> this._styleSource();
>>
>> },
>>
>>
>> // property apply
>>
>> _applyScale : function(value) {
>>
>> this._styleSource();
>>
>> },
>>
>>
>> /**
>>
>> * Remembers the mode to keep track which contentElement is currently
>> in
>>
>> * use.
>>
>> *
>>
>> * @param mode
>>
>> * {String} internal mode (alphaScaled|scaled|nonScaled)
>>
>> */
>>
>> __setMode : function(mode) {
>>
>> this.__mode = mode;
>>
>> },
>>
>>
>> /**
>>
>> * Returns the current mode if set. Otherwise checks the current
>> source and
>>
>> * the current scaling to determine the current mode.
>>
>> *
>>
>> * @return {String} current internal mode
>>
>> */
>>
>> __getMode : function() {
>>
>> if (this.__mode == null) {
>>
>> var source = this.getSource();
>>
>> var isPng = false;
>>
>> if (source != null) {
>>
>> isPng = qx.lang.String.endsWith(source, ".png");
>>
>> }
>>
>>
>> if (this.getScale() && isPng && qx.core.Environment.get(
>> "css.alphaimageloaderneeded")) {
>>
>> this.__mode = "alphaScaled";
>>
>> } else if (this.getScale()) {
>>
>> this.__mode = "scaled";
>>
>> } else {
>>
>> this.__mode = "nonScaled";
>>
>> }
>>
>> }
>>
>>
>> return this.__mode;
>>
>> },
>>
>>
>> /**
>>
>> * Creates a contentElement suitable for the current mode
>>
>> *
>>
>> * @param mode
>>
>> * {String} internal mode
>>
>> * @return {qx.html.Image} suitable image content element
>>
>> */
>>
>> __createSuitableContentElement : function(mode) {
>>
>> var scale;
>>
>> var tagName;
>>
>> if (mode == "alphaScaled") {
>>
>> scale = true;
>>
>> tagName = "div";
>>
>> } else if (mode == "nonScaled") {
>>
>> scale = false;
>>
>> tagName = "div";
>>
>> } else {
>>
>> scale = true;
>>
>> tagName = "img";
>>
>> }
>>
>>
>> var element = new qx.html.Image(tagName);
>>
>> element.setAttribute("$$widget", this.toHashCode());
>>
>> element.setScale(scale);
>>
>> element.setStyles({
>>
>> "overflowX" : "hidden",
>>
>> "overflowY" : "hidden",
>>
>> "boxSizing" : "border-box"
>>
>> });
>>
>>
>> if (qx.core.Environment.get("css.alphaimageloaderneeded")) {
>>
>> var wrapper = this.__wrapper = new qx.html.Element("div");
>>
>> wrapper.setAttribute("$$widget", this.toHashCode());
>>
>> wrapper.setStyle("position", "absolute");
>>
>> wrapper.add(element);
>>
>> return wrapper;
>>
>> }
>>
>>
>> return element;
>>
>> },
>>
>>
>> /**
>>
>> * Returns a contentElement suitable for the current mode
>>
>> *
>>
>> * @return {qx.html.Image} suitable image contentElement
>>
>> */
>>
>> __getSuitableContentElement : function() {
>>
>> if (this.$$disposed) {
>>
>> return null;
>>
>> }
>>
>>
>> var mode = this.__getMode();
>>
>>
>> if (this.__contentElements[mode] == null) {
>>
>> this.__contentElements[mode] = this
>> .__createSuitableContentElement(mode);
>>
>> }
>>
>>
>> var element = this.__contentElements[mode];
>>
>>
>> if (!this.__currentContentElement) {
>>
>> this.__currentContentElement = element;
>>
>> }
>>
>>
>> return element;
>>
>> },
>>
>>
>> /**
>>
>> * Applies the source to the clipped image instance or preload an
>> image to
>>
>> * detect sizes and apply it afterwards.
>>
>> *
>>
>> */
>>
>> _styleSource : function() {
>>
>> var source = qx.util.AliasManager.getInstance().resolve(this
>> .getSource());
>>
>>
>> var element = this.getContentElement();
>>
>> if (this.__wrapper) {
>>
>> element = element.getChild(0);
>>
>> }
>>
>>
>> if (!source) {
>>
>> element.resetSource();
>>
>> return;
>>
>> }
>>
>>
>> this.__checkForContentElementSwitch(source);
>>
>>
>> if ((qx.core.Environment.get("engine.name") == "mshtml")
>>
>> && (parseInt(qx.core.Environment.get("engine.version"), 10) <
>> 9 || qx.core.Environment.get("browser.documentmode") < 9)) {
>>
>> var repeat = this.getScale() ? "scale" : "no-repeat";
>>
>> element.tagNameHint =
>> qx.bom.element.Decoration.getTagName(repeat, source);
>>
>> }
>>
>>
>> var contentEl = this.__currentContentElement;
>>
>> if (this.__wrapper) {
>>
>> contentEl = contentEl.getChild(0);
>>
>> }
>>
>>
>> // Detect if the image registry knows this image
>>
>> if (qx.util.ResourceManager.getInstance().has(source)) {
>>
>> this.__setManagedImage(contentEl, source);
>>
>> } else if (qx.io.ImageLoader.isLoaded(source)) {
>>
>> this.__setUnmanagedImage(contentEl, source);
>>
>> } else {
>>
>> this.__loadUnmanagedImage(contentEl, source);
>>
>> }
>>
>> },
>>
>>
>> /**
>>
>> * Checks if the current content element is capable to display the
>> image
>>
>> * with the current settings (scaling, alpha PNG)
>>
>> *
>>
>> * @param source
>>
>> * {String} source of the image
>>
>> */
>>
>> __checkForContentElementSwitch : qx.core.Environment.select("
>> engine.name", {
>>
>> "mshtml" : function(source) {
>>
>> var alphaImageLoader = qx.core.Environment.get(
>> "css.alphaimageloaderneeded");
>>
>> var isPng = qx.lang.String.endsWith(source, ".png");
>>
>>
>> if (alphaImageLoader && isPng) {
>>
>> if (this.getScale() && this.__getMode() != "alphaScaled") {
>>
>> this.__setMode("alphaScaled");
>>
>> } else if (!this.getScale() && this.__getMode() != "nonScaled")
>> {
>>
>> this.__setMode("nonScaled");
>>
>> }
>>
>> } else {
>>
>> if (this.getScale() && this.__getMode() != "scaled") {
>>
>> this.__setMode("scaled");
>>
>> } else if (!this.getScale() && this.__getMode() != "nonScaled")
>> {
>>
>> this.__setMode("nonScaled");
>>
>> }
>>
>> }
>>
>>
>> this.__checkForContentElementReplacement(this
>> .__getSuitableContentElement());
>>
>> },
>>
>>
>> "default" : function(source) {
>>
>> if (this.getScale() && this.__getMode() != "scaled") {
>>
>> this.__setMode("scaled");
>>
>> } else if (!this.getScale() && this.__getMode("nonScaled")) {
>>
>> this.__setMode("nonScaled");
>>
>> }
>>
>>
>> this.__checkForContentElementReplacement(this
>> .__getSuitableContentElement());
>>
>> }
>>
>> }),
>>
>>
>> /**
>>
>> * Checks the current child and replaces it if necessary
>>
>> *
>>
>> * @param elementToAdd
>>
>> * {qx.html.Image} content element to add
>>
>> */
>>
>> __checkForContentElementReplacement : function(elementToAdd) {
>>
>> var currentContentElement = this.__currentContentElement;
>>
>>
>> if (currentContentElement != elementToAdd) {
>>
>> if (currentContentElement != null) {
>>
>> var pixel = "px";
>>
>> var styles = {};
>>
>>
>> // Copy dimension and location of the current content element
>>
>> var bounds = this.getBounds();
>>
>> if (bounds != null) {
>>
>> styles.width = bounds.width + pixel;
>>
>> styles.height = bounds.height + pixel;
>>
>> }
>>
>>
>> var insets = this.getInsets();
>>
>> styles.left = parseInt(currentContentElement.getStyle("left")
>> || insets.left) + pixel;
>>
>> styles.top = parseInt(currentContentElement.getStyle("top") ||
>> insets.top) + pixel;
>>
>>
>> styles.zIndex = 10;
>>
>>
>> var newEl = this.__wrapper ? elementToAdd.getChild(0) :
>> elementToAdd;
>>
>> newEl.setStyles(styles, true);
>>
>> newEl.setSelectable(this.getSelectable());
>>
>>
>> var container = currentContentElement.getParent();
>>
>>
>> if (container) {
>>
>> var index =
>> container.getChildren().indexOf(currentContentElement);
>>
>> container.removeAt(index);
>>
>> container.addAt(elementToAdd, index);
>>
>> }
>>
>> // force re-application of source so __setSource is called
>> again
>>
>> var hint = newEl.getNodeName();
>>
>> newEl.setSource(null);
>>
>> var currentEl = this.__wrapper ?
>> this.__currentContentElement.getChild(0)
>> : this.__currentContentElement;
>>
>> newEl.tagNameHint = hint;
>>
>> newEl.setAttribute("class", currentEl.getAttribute("class"));
>>
>>
>> // Flush elements to make sure the DOM elements are created.
>>
>> qx.html.Element.flush();
>>
>> var currentDomEl = currentEl.getDomElement();
>>
>> var newDomEl = elementToAdd.getDomElement();
>>
>> if (currentDomEl && newDomEl) {
>>
>> // Switch the DOM elements' hash codes. This is required for
>> the
>>
>> // event
>>
>> // layer to work [BUG #7447]
>>
>> var currentHash = currentDomEl.$$hash;
>>
>> currentDomEl.$$hash = newDomEl.$$hash;
>>
>> newDomEl.$$hash = currentHash;
>>
>> }
>>
>>
>> this.__currentContentElement = elementToAdd;
>>
>> }
>>
>> }
>>
>> },
>>
>>
>> /**
>>
>> * Use the ResourceManager to set a managed image
>>
>> *
>>
>> * @param el
>>
>> * {Element} image DOM element
>>
>> * @param source
>>
>> * {String} source path
>>
>> */
>>
>> __setManagedImage : function(el, source) {
>>
>> var ResourceManager = qx.util.ResourceManager.getInstance();
>>
>>
>> // Try to find a disabled image in registry
>>
>> if (!this.getEnabled()) {
>>
>> var disabled = source.replace(/\.([a-z]+)$/, "-disabled.$1");
>>
>> if (ResourceManager.has(disabled)) {
>>
>> source = disabled;
>>
>> this.addState("replacement");
>>
>> } else {
>>
>> this.removeState("replacement");
>>
>> }
>>
>> }
>>
>>
>> // Optimize case for enabled changes when no disabled image was
>> found
>>
>> if (el.getSource() === source) {
>>
>> return;
>>
>> }
>>
>>
>> // Apply source
>>
>> this.__setSource(el, source);
>>
>>
>> // Compare with old sizes and relayout if necessary
>>
>> this.__updateContentHint(ResourceManager.getImageWidth(source),
>> ResourceManager.getImageHeight(source));
>>
>> },
>>
>>
>> /**
>>
>> * Use the infos of the
>>
> ------------------------------------------------------------------------------
> Site24x7 APM Insight: Get Deep Visibility into Application Performance APM
> + Mobile APM + RUM: Monitor 3 App instances at just $35/Month Monitor
> end-to-end web transactions and take corrective actions now Troubleshoot
> faster and improve end-user experience. Signup Now!
> http://pubads.g.doubleclick.net/gampad/clk?id=267308311&iu=/4140_______________________________________________
> qooxdoo-devel mailing list qooxdoo-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel
>
> ------------------------------------------------------------------------------
> Site24x7 APM Insight: Get Deep Visibility into Application Performance
> APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
> Monitor end-to-end web transactions and take corrective actions now
> Troubleshoot faster and improve end-user experience. Signup Now!
> http://pubads.g.doubleclick.net/gampad/clk?id=267308311&iu=/4140
> _______________________________________________
> qooxdoo-devel mailing list
> qooxdoo-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel
>
------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance
APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
Monitor end-to-end web transactions and take corrective actions now
Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=267308311&iu=/4140
_______________________________________________
qooxdoo-devel mailing list
qooxdoo-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel