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