Discussion:
[qooxdoo-devel] Scaling (but not skewing) Atom image
Reed
2016-01-11 20:35:08 UTC
Permalink
Hello wonderful community,

I am unable to effect what I want with an Atom. I have an image which will
be of variable dimensions. I need it to display with the proper proportions
always, but with a maximum width and height. If either the width or height
are above the maximum, the image should scale down to fit, but maintain
proportions. In vanilla HTML/CSS, I would simply set max-width and
max-height, and neither explicit width nor height. See this codepen for an
example of my desired effect: http://codepen.io/reedspool/pen/adwyvB

With an Atom image, however, I'm unable to make this happen. I've played
with the properties scale, allowShrinkX, allowShrinkY, maxWidth, maxHeight,
allowStretchX, and allowStretchY. None of my experiments has worked. See
this playground link, http://goo.gl/pHcUcE

As you can see, the best of I've gotten is to maintain proportions by
setting scale to false, but the maxWidth doesn't work as I'd expect (from
experience with vanilla HTML/CSS), and the image is cut off.

I've also attempted the image manipulation with a `qx.ui.embed.Html()`
element as well, with little success. See this playground link,
http://goo.gl/cZoYdC Here, the image doesn't show at all (I think because
I'm not setting the container height properly.

Any suggestions appreciated, either to fix my current approaches or offer a
new one.

Thanks,
Reed
Dietrich Streifert
2016-01-12 08:17:24 UTC
Permalink
Hi Reed

qx.ui.basic.Atom is (I think) not intended to be used that way.

I'll use instead a qx.ui.basic.Label, set the rich property to true
which allows you to use regular html as the label value:

http://tinyurl.com/jk897lh
Post by Reed
Hello wonderful community,
I am unable to effect what I want with an Atom. I have an image which
will be of variable dimensions. I need it to display with the proper
proportions always, but with a maximum width and height. If either the
width or height are above the maximum, the image should scale down to
fit, but maintain proportions. In vanilla HTML/CSS, I would simply set
max-width and max-height, and neither explicit width nor height. See
http://codepen.io/reedspool/pen/adwyvB
With an Atom image, however, I'm unable to make this happen. I've
played with the properties scale, allowShrinkX, allowShrinkY,
maxWidth, maxHeight, allowStretchX, and allowStretchY. None of my
experiments has worked. See this playground link, http://goo.gl/pHcUcE
As you can see, the best of I've gotten is to maintain proportions by
setting scale to false, but the maxWidth doesn't work as I'd expect
(from experience with vanilla HTML/CSS), and the image is cut off.
I've also attempted the image manipulation with a `qx.ui.embed.Html()`
element as well, with little success. See this playground link,
http://goo.gl/cZoYdC Here, the image doesn't show at all (I think
because I'm not setting the container height properly.
Any suggestions appreciated, either to fix my current approaches or
offer a new one.
Thanks,
Reed
------------------------------------------------------------------------------
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
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel
Derrell Lipman
2016-01-12 12:51:58 UTC
Permalink
Reed, you probably have to click Run twice to view this since the image is
not initially loaded and doesn't appear initially. Also, I think Dietrich
intended max-height vs maxHeight and max-width vs maxWidth in the image.

Dietrich, thanks! Unfortunately, this doesn't do what Reed needs and I
haven't come up with a good solution either. In your playground example,
when the window is reduced in size in one dimension, the image does not
shrink to fit so that the image is fully visible. Instead, part of the
image is cut off. Reed's goal here is to display an entire image *in its
original aspect ratio*, however small as necessary to be fully visible in
the provided space. Additional suggestions appreciated!

Derrell


On Tue, Jan 12, 2016 at 3:17 AM Dietrich Streifert <
Post by Dietrich Streifert
Hi Reed
qx.ui.basic.Atom is (I think) not intended to be used that way.
I'll use instead a qx.ui.basic.Label, set the rich property to true which
http://tinyurl.com/jk897lh
Hello wonderful community,
I am unable to effect what I want with an Atom. I have an image which will
be of variable dimensions. I need it to display with the proper proportions
always, but with a maximum width and height. If either the width or height
are above the maximum, the image should scale down to fit, but maintain
proportions. In vanilla HTML/CSS, I would simply set max-width and
max-height, and neither explicit width nor height. See this codepen for an
example of my desired effect: http://codepen.io/reedspool/pen/adwyvB
With an Atom image, however, I'm unable to make this happen. I've played
with the properties scale, allowShrinkX, allowShrinkY, maxWidth, maxHeight,
allowStretchX, and allowStretchY. None of my experiments has worked. See
this playground link, http://goo.gl/pHcUcE
As you can see, the best of I've gotten is to maintain proportions by
setting scale to false, but the maxWidth doesn't work as I'd expect (from
experience with vanilla HTML/CSS), and the image is cut off.
I've also attempted the image manipulation with a `qx.ui.embed.Html()`
element as well, with little success. See this playground link,
http://goo.gl/cZoYdC Here, the image doesn't show at all (I think because
I'm not setting the container height properly.
Any suggestions appreciated, either to fix my current approaches or offer
a new one.
Thanks,
Reed
------------------------------------------------------------------------------
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
_______________________________________________
------------------------------------------------------------------------------
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
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel
John Spackman
2016-01-12 13:09:04 UTC
Permalink
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 ImageLoader to set an unmanaged image

*

* @param el

* {Element} image DOM element

* @param source

* {String} source path

*/

__setUnmanagedImage : function(el, source) {

var ImageLoader = qx.io.ImageLoader;



// Apply source

this.__setSource(el, source);



// Compare with old sizes and relayout if necessary

var width = ImageLoader.getWidth(source);

var height = ImageLoader.getHeight(source);

this.__updateContentHint(width, height);

},



/**

* Use the ImageLoader to load an unmanaged image

*

* @param el

* {Element} image DOM element

* @param source

* {String} source path

*/

__loadUnmanagedImage : function(el, source) {

var ImageLoader = qx.io.ImageLoader;



if (qx.core.Environment.get("qx.debug")) {

// loading external images via HTTP/HTTPS is a common usecase, as is

// using data URLs.

var sourceLC = source.toLowerCase();

var startsWith = qx.lang.String.startsWith;

if (!startsWith(sourceLC, "http") && !startsWith(sourceLC, "data:image/")) {

var self = this.self(arguments);



if (!self.__warned) {

self.__warned = {};

}



if (!self.__warned[source]) {

this.debug("try to load an unmanaged relative image: " + source);

self.__warned[source] = true;

}

}

}



// only try to load the image if it not already failed

if (!ImageLoader.isFailed(source)) {

ImageLoader.load(source, this.__loaderCallback, this);

} else {

if (el != null) {

el.resetSource();

}

}

},



/**

* Combines the decorator's image styles with our own image to make sure

* gradient and backgroundImage decorators work on Images.

*

* @param el

* {Element} image DOM element

* @param source

* {String} source path

*/

__setSource : function(el, source) {

if (el.getNodeName() == "div") {



var dec = qx.theme.manager.Decoration.getInstance().resolve(this.getDecorator());

// if the decorator defines any CSS background-image

if (dec) {

var hasGradient = (dec.getStartColor() && dec.getEndColor());

var hasBackground = dec.getBackgroundImage();

if (hasGradient || hasBackground) {

var repeat = this.getScale() ? "scale" : "no-repeat";



// get the style attributes for the given source

var attr = qx.bom.element.Decoration.getAttributes(source, repeat);

// get the background image(s) defined by the decorator

var decStyle = dec.getStyles(true);



var combinedStyles = {

"backgroundImage" : attr.style.backgroundImage,

"backgroundPosition" : (attr.style.backgroundPosition || "0 0"),

"backgroundRepeat" : (attr.style.backgroundRepeat || "no-repeat")

};



if (hasBackground) {

combinedStyles["backgroundPosition"] += "," + decStyle["background-position"] || "0 0";

combinedStyles["backgroundRepeat"] += ", " + dec.getBackgroundRepeat();

}



if (hasGradient) {

combinedStyles["backgroundPosition"] += ", 0 0";

combinedStyles["backgroundRepeat"] += ", no-repeat";

}



combinedStyles["backgroundImage"] += "," + decStyle["background-image"];



// apply combined background images

el.setStyles(combinedStyles);



return;

}

} else {

// force re-apply to remove old decorator styles

el.setSource(null);

}

}



el.setSource(source);

},



/**

* Event handler fired after the preloader has finished loading the icon

*

* @param source

* {String} Image source which was loaded

* @param imageInfo

* {Map} Dimensions of the loaded image

*/

__loaderCallback : function(source, imageInfo) {

// Ignore the callback on already disposed images

if (this.$$disposed === true) {

return;

}



// Ignore when the source has already been modified

if (source !== qx.util.AliasManager.getInstance().resolve(this.getSource())) {

return;

}



// Output a warning if the image could not loaded and quit

if (imageInfo.failed) {

this.warn("Image could not be loaded: " + source);

this.fireEvent("loadingFailed");

} else if (imageInfo.aborted) {

// ignore the rest because it is aborted

return;

} else {

this.fireEvent("loaded");

}



// Update image (again)

this._styleSource();

},



/**

* Updates the content hint when the image size has been changed

*

* @param width

* {Integer} width of the image

* @param height

* {Integer} height of the image

*/

__updateContentHint : function(width, height) {

if (width === undefined || height == undefined) {

width = this.__width;

height = this.__height;

}

if (this._recalc(width, height))

qx.ui.core.queue.Layout.add(this);

},





/**

* Recalculates the size of the image, according to scaling parameters

* @param maxWidth {Integer?} maximum width restriction

* @param maxHeight {Integer?} minimum height restriction

*/

_recalc: function(originalWidth, originalHeight) {

var fixedWidth = this.getWidth();

var fixedHeight = this.getHeight();

var newHeight = originalHeight;

var newWidth = originalWidth;

var maxWidth = this.getMaxWidth();

var maxHeight = this.getMaxHeight();

var minWidth = this.getMinWidth();

var minHeight = this.getMinHeight();



if (!this.isAllowScaleUp()) {

if (maxHeight > originalHeight)

maxHeight = originalHeight;

if (maxWidth > originalWidth)

maxWidth = originalWidth;

}





switch(this.getForceRatio()) {

case 'height':

if (fixedWidth)

newHeight = fixedWidth * originalHeight / originalWidth;

break;



case 'width':

if (fixedHeight)

newWidth = fixedHeight * originalWidth / originalHeight;

break;



case 'auto':

if (fixedWidth)

newHeight = fixedWidth * originalHeight / originalWidth;

if (fixedHeight)

newWidth = fixedHeight * originalWidth / originalHeight;



if (maxWidth && newWidth > maxWidth) {

newWidth = maxWidth;

newHeight = newWidth * originalHeight / originalWidth;

}

if (maxHeight && newHeight > maxHeight) {

newHeight = maxHeight;

newWidth = newHeight * originalWidth / originalHeight;

if (maxWidth && newWidth > maxWidth) {

newWidth = maxWidth;

newHeight = newWidth * originalHeight / originalWidth;

}

}

newHeight = newWidth * originalHeight / originalWidth;

break;

}



var width = parseInt(newWidth);

var height = parseInt(newHeight);

if (width != this.__width || height != this.__height) {

this.__width = width;

this.__height = height;

return true;

}

return false;

}

},



/*

* ****************************************************************************

* DESTRUCTOR

* ****************************************************************************

*/



destruct : function() {

delete this.__currentContentElement;

this._disposeMap("__contentElements");

}

});



var SMILEY_URL = "Loading Image...";



var container = new qx.ui.container.Composite(new qx.ui.layout.VBox());

this.getRoot().add(container);



var image = new grasshopper.af.ui.image.Image(SMILEY_URL).set({

maxWidth: 200

});





// Add the atom to the page

container.add(image);
Derrell Lipman
2016-01-12 13:25:29 UTC
Permalink
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
Post by John Spackman
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
/**
* 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
*
****************************************************************************
*/
/**
* {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.
*
* {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.
*
*/
__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
*
* {String} internal mode
*/
__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
*
*/
__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)
*
* {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
*
* {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
*
* {Element} image DOM element
* {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
John Spackman
2016-01-12 16:45:39 UTC
Permalink
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 <***@unwireduniverse.com>
Reply-To: qooxdoo Development <qooxdoo-***@lists.sourceforge.net>
Date: Tuesday, 12 January 2016 at 13:25
To: qooxdoo Development <qooxdoo-***@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-***@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-***@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel
Derrell Lipman
2016-01-12 16:48:23 UTC
Permalink
Thanks, John! This would be a nice contribution to core qooxdoo.

D
Post by John Spackman
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.
Date: Tuesday, 12 January 2016 at 13:25
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
Post by John Spackman
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
/**
* 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
*
****************************************************************************
*/
/**
* {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.
*
* {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.
*
*/
__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
*
* {String} internal mode
*/
__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
*
*/
__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)
*
* {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
*
* {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
*
* {Element} image DOM element
* {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_______________________________________________
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
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel
Reed
2016-01-12 17:01:58 UTC
Permalink
Thanks everyone,

John, your code does precisely what I need! Look forward to seeing it
proposed for core, if possible.

Thanks John, Dietrich, and Derrell,
Reed

On Tue, Jan 12, 2016 at 11:48 AM, Derrell Lipman <
Post by Derrell Lipman
Thanks, John! This would be a nice contribution to core qooxdoo.
D
Post by John Spackman
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.
Date: Tuesday, 12 January 2016 at 13:25
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
Post by John Spackman
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
/**
* 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
*
****************************************************************************
*/
/**
* {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.
*
* {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.
*
*/
__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
*
* {String} internal mode
*/
__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
*
*/
__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)
*
* {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
*
* {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
*
* {Element} image DOM element
* {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_______________________________________________
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
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
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel
SQville
2016-01-13 00:26:17 UTC
Permalink
I was able to match the functionality by changing the image to an svg file
and scale = true - http://tinyurl.com/z8uy7ne

Not sure if you are able to go svg vs. png



--
View this message in context: http://qooxdoo.678.n2.nabble.com/Scaling-but-not-skewing-Atom-image-tp7587939p7587947.html
Sent from the qooxdoo mailing list archive at Nabble.com.
Reed
2016-01-13 00:59:43 UTC
Permalink
This is really interesting. I expected the scale property to work exactly
like this!

Unfortunately, the actual images we'll be displaying are not simple smileys
but high-resolution raster images.

Thanks regardless,
Reed
Post by SQville
I was able to match the functionality by changing the image to an svg file
and scale = true - http://tinyurl.com/z8uy7ne
Not sure if you are able to go svg vs. png
--
http://qooxdoo.678.n2.nabble.com/Scaling-but-not-skewing-Atom-image-tp7587939p7587947.html
Sent from the qooxdoo mailing list archive at Nabble.com.
------------------------------------------------------------------------------
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
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel
Loading...