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

Reply via email to