Discussion:
cursor on images with scale true
(too old to reply)
kreimer
2016-02-15 16:15:48 UTC
Permalink
Hi,

I discover that giving true to the scale property of an qx.ui.basic.Image,
then the cursor property seems to be ignored.

Here, the playground link:

http://tinyurl.com/znvwv86

Cheers!



--
View this message in context: http://qooxdoo.678.n2.nabble.com/cursor-on-images-with-scale-true-tp7588002.html
Sent from the qooxdoo mailing list archive at Nabble.com.
kreimer
2016-02-18 15:10:20 UTC
Permalink
Yes, indeed... e.g backgroundColor is also affected.

http://tinyurl.com/jstq429

trying to hack the qx.ui.basic.Image to solve this... but I don't fully
understand what _styleSource does...




--
View this message in context: http://qooxdoo.678.n2.nabble.com/cursor-on-images-with-scale-true-tp7588002p7588049.html
Sent from the qooxdoo mailing list archive at Nabble.com.
kreimer
2016-02-23 13:49:57 UTC
Permalink
Hi!

First, I want to apologise for having pasted all this code in this post, but
tinyurl said "414 Request-URI Too Large"

In order to hack a bit the qx.ui.basic.Image code.. I had to copy the source
in the playgground into a new class "kreimer.MyImage2"

inserting a few lines in the __checkForContentElementReplacement private
method:
// kreimer
var currentStyles = currentContentElement.getAllStyles();
this.info("currentStyles" + currentStyles);
for(var prop in currentStyles) {
styles[prop] = currentStyles[prop];
}
//kreimer

I don't know for sure if its the best solution, it's not fully tested, but
it seems to solve the issue. Can somebody try this? Thanks everybody in the
community!

Here, the code to paste into the playground:

qx.Class.define("kreimer.MyImage2", {
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. Setting it will possibly abort loading of
current 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 : false,
themeable : true,
apply : "_applyScale"
},


// 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. This event can only be
* fired for the first loading of an unmanaged resource (external
image).
*/
loadingFailed : "qx.event.type.Event",


/**
* Fired if the image has been loaded. This is even true for managed
* resources (images known by generator).
*/
loaded : "qx.event.type.Event",


/** Fired when the pending request has been aborted. */
aborted : "qx.event.type.Event"
},


statics:
{
PLACEHOLDER_IMAGE:
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
},

/*

*****************************************************************************
MEMBERS

*****************************************************************************
*/

members :
{
__width : null,
__height : null,
__mode : null,
__contentElements : null,
__currentContentElement : null,
__wrapper : null,
__requestId : 0,


//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
};
},

// 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, old)
{
// abort loading current image
if (old) {
if (qx.io.ImageLoader.isLoading(old)) {
qx.io.ImageLoader.abort(old);
}
}

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 AliasManager = qx.util.AliasManager.getInstance();
var ResourceManager = qx.util.ResourceManager.getInstance();

var source = AliasManager.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.__getContentElement();

// Detect if the image registry knows this image
if (qx.util.ResourceManager.getInstance().has(source)) {
var highResolutionSource = this._findHighResolutionSource(source);
if (highResolutionSource) {
var imageWidth = ResourceManager.getImageHeight(source);
var imageHeight = ResourceManager.getImageWidth(source);
this.setWidth(imageWidth);
this.setHeight(imageHeight);

// set backgroud size on current element (div or img)
var backgroundSize = imageWidth + "px, " + imageHeight + "px";
this.__currentContentElement.setStyle("background-size",
backgroundSize);

this.setSource(highResolutionSource);
source = highResolutionSource;
}
this.__setManagedImage(contentEl, source);
this.__fireLoadEvent();
} else if (qx.io.ImageLoader.isLoaded(source)) {
this.__setUnmanagedImage(contentEl, source);
this.__fireLoadEvent();
} else {
this.__loadUnmanagedImage(contentEl, source);
}
},


/**
* Helper function, which fires <code>loaded</code> event
asynchronously.
* It emulates native <code>loaded</code> event of an image object. This
* helper will be called, if you try to load a managed image or an
* previously loaded unmanaged image.
*/
__fireLoadEvent : function()
{
this.__requestId++;
qx.bom.AnimationFrame.request(function(rId){
// prevent firing of the event if source changed in the meantime
if (rId === this.__requestId) {
this.fireEvent("loaded");
} else {
this.fireEvent("aborted");
}
}.bind(this, this.__requestId));
},


/**
* Returns the content element.
* @return {qx.html.Image} content element
*/
__getContentElement : function()
{
var contentEl = this.__currentContentElement;
if (this.__wrapper) {
contentEl = contentEl.getChild(0);
}

return contentEl;
},


/**
* 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 = {};

// kreimer
var currentStyles = currentContentElement.getAllStyles();
this.info("currentStyles" + currentStyles);
for(var prop in currentStyles) {
styles[prop] = currentStyles[prop];
}
//kreimer

// 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());

if (!currentContentElement.isVisible()) {
elementToAdd.hide();
}

if (!currentContentElement.isIncluded()) {
elementToAdd.exclude();
}

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.__getContentElement();
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();

// copy event listeners
var listeners = currentContentElement.getListeners() || [];
listeners.forEach(function(listenerData) {
elementToAdd.addListener(listenerData.type,
listenerData.handler, listenerData.self, listenerData.capture);
});

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") {

// checks if a decorator already set.
// In this case we have to merge background styles
var decorator =
qx.theme.manager.Decoration.getInstance().resolve(this.getDecorator());
if (decorator) {
var hasGradient = (decorator.getStartColor() &&
decorator.getEndColor());
var hasBackground = decorator.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 decoratorStyle = decorator.getStyles(true);

var combinedStyles = {
"backgroundImage": attr.style.backgroundImage,
"backgroundPosition": (attr.style.backgroundPosition || "0
0"),
"backgroundRepeat": (attr.style.backgroundRepeat ||
"no-repeat")
};

if (hasBackground) {
combinedStyles["backgroundPosition"] += "," +
decoratorStyle["background-position"] || "0 0";
combinedStyles["backgroundRepeat"] += ", " +
decorator.getBackgroundRepeat();
}

if (hasGradient) {
combinedStyles["backgroundPosition"] += ", 0 0";
combinedStyles["backgroundRepeat"] += ", no-repeat";
}

combinedStyles["backgroundImage"] += "," +
decoratorStyle["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);
},

/**
* Detects whether there is a high-resolution image available.
* A high-resolution image is assumed to have the same file name as
* the parameter source, but with a pixelRatio identifier before the
file
* extension, like "@2x".
* Medium Resolution: "example.png", high-resolution: "***@2x.png"
*
* @param lowResImgSrc {String} source of the low resolution image.
* @return {String|Boolean} If a high-resolution image source.
*/
_findHighResolutionSource: function(lowResImgSrc) {
var pixelRatioCandidates = ["3", "2", "1.5"];

// Calculate the optimal ratio, based on the rem scale factor of the
application and the device pixel ratio.
var factor =
parseFloat(qx.bom.client.Device.getDevicePixelRatio().toFixed(2));
if (factor <= 1) {
return false;
}

var i = pixelRatioCandidates.length;
while (i > 0 && factor > pixelRatioCandidates[--i]) {}

var hiResImgSrc;
var k;

// Search for best img with a higher resolution.
for (k = i; k >= 0; k--) {
hiResImgSrc = this._getHighResolutionSource(lowResImgSrc,
pixelRatioCandidates[k]);
if (hiResImgSrc) {
return hiResImgSrc;
}
}

// Search for best img with a lower resolution.
for (k = i + 1; k < pixelRatioCandidates.length; k++) {
hiResImgSrc = this._getHighResolutionSource(lowResImgSrc,
pixelRatioCandidates[k]);
if (hiResImgSrc) {
return hiResImgSrc;
}
}

return null;
},

/**
* Returns the source name for the high-resolution image based on the
passed
* parameters.
* @param source {String} the source of the medium resolution image.
* @param pixelRatio {Number} the pixel ratio of the high-resolution
image.
* @return {String} the high-resolution source name or null if no source
could be found.
*/
_getHighResolutionSource : function(source, pixelRatio) {
var fileExtIndex = source.lastIndexOf('.');
if (fileExtIndex > -1) {
var pixelRatioIdentifier = "@" + pixelRatio + "x";
var candidate = source.slice(0, fileExtIndex) + pixelRatioIdentifier
+ source.slice(fileExtIndex);

if(qx.util.ResourceManager.getInstance().has(candidate)) {
return candidate;
}
}
return null;
},

/**
* 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())) {
this.fireEvent("aborted");
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) {
this.fireEvent("aborted");
return;
} else {
this.fireEvent("loaded");
}

// Update image
this.__setUnmanagedImage(this.__getContentElement(), source);
},


/**
* 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)
{
// Compare with old sizes and relayout if necessary
if (width !== this.__width || height !== this.__height)
{
this.__width = width;
this.__height = height;

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


/*

*****************************************************************************
DESTRUCTOR

*****************************************************************************
*/

destruct : function() {
for (var mode in this.__contentElements) {
if (this.__contentElements.hasOwnProperty(mode)) {
this.__contentElements[mode].setAttribute("$$widget", null, true);
}
}

delete this.__currentContentElement;
if (this.__wrapper) {
delete this.__wrapper;
}

this._disposeMap("__contentElements");
}
});




var btn = new qx.ui.form.CheckBox("Image#scale").set({ value : true});
var setSrcBtn = new qx.ui.form.Button("setGreen");
setSrcBtn.addListener("execute", function(e) {
img.setBackgroundColor("green");
}, this);

var img = new
kreimer.MyImage2("Loading Image...").set({
cursor : "pointer",
backgroundColor : "orange",
height : 64,
width : 64,
scale : true
});

var doc = this.getRoot();
doc.add(btn, { left: 10, top: 10 });
doc.add(setSrcBtn, { left: 150, top: 10 });
doc.add(img, { left: 10, top: 50 });
btn.bind("value", img, "scale");




--
View this message in context: http://qooxdoo.678.n2.nabble.com/cursor-on-images-with-scale-true-tp7588002p7588101.html
Sent from the qooxdoo mailing list archive at Nabble.com.
William Oprandi
2016-02-24 08:12:03 UTC
Permalink
I can confirm this. I don't know why, but in my own app, I have not always
this issue I suppose this is a bug.
Post by kreimer
Hi!
First, I want to apologise for having pasted all this code in this post, but
tinyurl said "414 Request-URI Too Large"
In order to hack a bit the qx.ui.basic.Image code.. I had to copy the source
in the playgground into a new class "kreimer.MyImage2"
inserting a few lines in the __checkForContentElementReplacement private
// kreimer
var currentStyles = currentContentElement.getAllStyles();
this.info("currentStyles" + currentStyles);
for(var prop in currentStyles) {
styles[prop] = currentStyles[prop];
}
//kreimer
I don't know for sure if its the best solution, it's not fully tested, but
it seems to solve the issue. Can somebody try this? Thanks everybody in the
community!
qx.Class.define("kreimer.MyImage2", {
extend : qx.ui.core.Widget,
/*
*****************************************************************************
CONSTRUCTOR
*****************************************************************************
*/
/**
*/
construct : function(source)
{
this.__contentElements = {};
this.base(arguments);
if (source) {
this.setSource(source);
}
},
/*
*****************************************************************************
PROPERTIES
*****************************************************************************
*/
{
/** The URL of the image. Setting it will possibly abort loading of
current image. */
{
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.
*/
{
check : "Boolean",
init : false,
themeable : true,
apply : "_applyScale"
},
// overridden
{
refine : true,
init : "image"
},
// overridden
{
refine : true,
init : false
},
// overridden
{
refine : true,
init : false
},
// overridden
{
refine : true,
init : false
},
// overridden
{
refine : true,
init : false
}
},
/*
*****************************************************************************
EVENTS
*****************************************************************************
*/
{
/**
* Fired if the image source can not be loaded. This event can only be
* fired for the first loading of an unmanaged resource (external
image).
*/
loadingFailed : "qx.event.type.Event",
/**
* Fired if the image has been loaded. This is even true for managed
* resources (images known by generator).
*/
loaded : "qx.event.type.Event",
/** Fired when the pending request has been aborted. */
aborted : "qx.event.type.Event"
},
{
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
},
/*
*****************************************************************************
MEMBERS
*****************************************************************************
*/
{
__width : null,
__height : null,
__mode : null,
__contentElements : null,
__currentContentElement : null,
__wrapper : null,
__requestId : 0,
//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
};
},
// 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, old)
{
// abort loading current image
if (old) {
if (qx.io.ImageLoader.isLoading(old)) {
qx.io.ImageLoader.abort(old);
}
}
this._styleSource();
},
// property apply
_applyScale : function(value) {
this._styleSource();
},
/**
* Remembers the mode to keep track which contentElement is currently in
use.
*/
__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
*
*/
__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 AliasManager = qx.util.AliasManager.getInstance();
var ResourceManager = qx.util.ResourceManager.getInstance();
var source = AliasManager.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.__getContentElement();
// Detect if the image registry knows this image
if (qx.util.ResourceManager.getInstance().has(source)) {
var highResolutionSource = this._findHighResolutionSource(source);
if (highResolutionSource) {
var imageWidth = ResourceManager.getImageHeight(source);
var imageHeight = ResourceManager.getImageWidth(source);
this.setWidth(imageWidth);
this.setHeight(imageHeight);
// set backgroud size on current element (div or img)
var backgroundSize = imageWidth + "px, " + imageHeight + "px";
this.__currentContentElement.setStyle("background-size",
backgroundSize);
this.setSource(highResolutionSource);
source = highResolutionSource;
}
this.__setManagedImage(contentEl, source);
this.__fireLoadEvent();
} else if (qx.io.ImageLoader.isLoaded(source)) {
this.__setUnmanagedImage(contentEl, source);
this.__fireLoadEvent();
} else {
this.__loadUnmanagedImage(contentEl, source);
}
},
/**
* Helper function, which fires <code>loaded</code> event
asynchronously.
* It emulates native <code>loaded</code> event of an image object. This
* helper will be called, if you try to load a managed image or an
* previously loaded unmanaged image.
*/
__fireLoadEvent : function()
{
this.__requestId++;
qx.bom.AnimationFrame.request(function(rId){
// prevent firing of the event if source changed in the meantime
if (rId === this.__requestId) {
this.fireEvent("loaded");
} else {
this.fireEvent("aborted");
}
}.bind(this, this.__requestId));
},
/**
* Returns the content element.
*/
__getContentElement : function()
{
var contentEl = this.__currentContentElement;
if (this.__wrapper) {
contentEl = contentEl.getChild(0);
}
return contentEl;
},
/**
* Checks if the current content element is capable to display the image
* with the current settings (scaling, alpha PNG)
*
*/
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
*
*/
__checkForContentElementReplacement : function(elementToAdd)
{
var currentContentElement = this.__currentContentElement;
if (currentContentElement != elementToAdd)
{
if (currentContentElement != null)
{
var pixel = "px";
var styles = {};
// kreimer
var currentStyles = currentContentElement.getAllStyles();
this.info("currentStyles" + currentStyles);
for(var prop in currentStyles) {
styles[prop] = currentStyles[prop];
}
//kreimer
// 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;
elementToAdd;
newEl.setStyles(styles, true);
newEl.setSelectable(this.getSelectable());
if (!currentContentElement.isVisible()) {
elementToAdd.hide();
}
if (!currentContentElement.isIncluded()) {
elementToAdd.exclude();
}
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.__getContentElement();
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();
// copy event listeners
var listeners = currentContentElement.getListeners() || [];
listeners.forEach(function(listenerData) {
elementToAdd.addListener(listenerData.type,
listenerData.handler, listenerData.self, listenerData.capture);
});
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
*
*/
__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
*
*/
__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
*
*/
__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.
*
*/
__setSource: function (el, source) {
if (el.getNodeName() == "div") {
// checks if a decorator already set.
// In this case we have to merge background styles
var decorator =
qx.theme.manager.Decoration.getInstance().resolve(this.getDecorator());
if (decorator) {
var hasGradient = (decorator.getStartColor() &&
decorator.getEndColor());
var hasBackground = decorator.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 decoratorStyle = decorator.getStyles(true);
var combinedStyles = {
"backgroundImage": attr.style.backgroundImage,
"backgroundPosition": (attr.style.backgroundPosition || "0
0"),
"backgroundRepeat": (attr.style.backgroundRepeat ||
"no-repeat")
};
if (hasBackground) {
combinedStyles["backgroundPosition"] += "," +
decoratorStyle["background-position"] || "0 0";
combinedStyles["backgroundRepeat"] += ", " +
decorator.getBackgroundRepeat();
}
if (hasGradient) {
combinedStyles["backgroundPosition"] += ", 0 0";
combinedStyles["backgroundRepeat"] += ", no-repeat";
}
combinedStyles["backgroundImage"] += "," +
decoratorStyle["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);
},
/**
* Detects whether there is a high-resolution image available.
* A high-resolution image is assumed to have the same file name as
* the parameter source, but with a pixelRatio identifier before the
file
*
*/
_findHighResolutionSource: function(lowResImgSrc) {
var pixelRatioCandidates = ["3", "2", "1.5"];
// Calculate the optimal ratio, based on the rem scale factor of the
application and the device pixel ratio.
var factor =
parseFloat(qx.bom.client.Device.getDevicePixelRatio().toFixed(2));
if (factor <= 1) {
return false;
}
var i = pixelRatioCandidates.length;
while (i > 0 && factor > pixelRatioCandidates[--i]) {}
var hiResImgSrc;
var k;
// Search for best img with a higher resolution.
for (k = i; k >= 0; k--) {
hiResImgSrc = this._getHighResolutionSource(lowResImgSrc,
pixelRatioCandidates[k]);
if (hiResImgSrc) {
return hiResImgSrc;
}
}
// Search for best img with a lower resolution.
for (k = i + 1; k < pixelRatioCandidates.length; k++) {
hiResImgSrc = this._getHighResolutionSource(lowResImgSrc,
pixelRatioCandidates[k]);
if (hiResImgSrc) {
return hiResImgSrc;
}
}
return null;
},
/**
* Returns the source name for the high-resolution image based on the
passed
* parameters.
image.
could be found.
*/
_getHighResolutionSource : function(source, pixelRatio) {
var fileExtIndex = source.lastIndexOf('.');
if (fileExtIndex > -1) {
var candidate = source.slice(0, fileExtIndex) +
pixelRatioIdentifier
+ source.slice(fileExtIndex);
if(qx.util.ResourceManager.getInstance().has(candidate)) {
return candidate;
}
}
return null;
},
/**
* Event handler fired after the preloader has finished loading the icon
*
*/
__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())) {
this.fireEvent("aborted");
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) {
this.fireEvent("aborted");
return;
} else {
this.fireEvent("loaded");
}
// Update image
this.__setUnmanagedImage(this.__getContentElement(), source);
},
/**
* Updates the content hint when the image size has been changed
*
*/
__updateContentHint : function(width, height)
{
// Compare with old sizes and relayout if necessary
if (width !== this.__width || height !== this.__height)
{
this.__width = width;
this.__height = height;
qx.ui.core.queue.Layout.add(this);
}
}
},
/*
*****************************************************************************
DESTRUCTOR
*****************************************************************************
*/
destruct : function() {
for (var mode in this.__contentElements) {
if (this.__contentElements.hasOwnProperty(mode)) {
this.__contentElements[mode].setAttribute("$$widget", null, true);
}
}
delete this.__currentContentElement;
if (this.__wrapper) {
delete this.__wrapper;
}
this._disposeMap("__contentElements");
}
});
var btn = new qx.ui.form.CheckBox("Image#scale").set({ value : true});
var setSrcBtn = new qx.ui.form.Button("setGreen");
setSrcBtn.addListener("execute", function(e) {
img.setBackgroundColor("green");
}, this);
var img = new
kreimer.MyImage2("
http://support.showreelfinder.com/hc/en-us/article_attachments/201500876/reset.png
").set({
cursor : "pointer",
backgroundColor : "orange",
height : 64,
width : 64,
scale : true
});
var doc = this.getRoot();
doc.add(btn, { left: 10, top: 10 });
doc.add(setSrcBtn, { left: 150, top: 10 });
doc.add(img, { left: 10, top: 50 });
btn.bind("value", img, "scale");
--
http://qooxdoo.678.n2.nabble.com/cursor-on-images-with-scale-true-tp7588002p7588101.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=272487151&iu=/4140
_______________________________________________
qooxdoo-devel mailing list
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel
--
W.Oprandi
***@gmail.com
John Spackman
2016-02-24 09:02:34 UTC
Permalink
Hi Bernardo

That looks like a sensible fix - please can you submit it as a pull request?

Cheers
John
Post by kreimer
Hi!
First, I want to apologise for having pasted all this code in this post, but
tinyurl said "414 Request-URI Too Large"
In order to hack a bit the qx.ui.basic.Image code.. I had to copy the source
in the playgground into a new class "kreimer.MyImage2"
inserting a few lines in the __checkForContentElementReplacement private
// kreimer
var currentStyles = currentContentElement.getAllStyles();
this.info("currentStyles" + currentStyles);
for(var prop in currentStyles) {
styles[prop] = currentStyles[prop];
}
//kreimer
I don't know for sure if its the best solution, it's not fully tested, but
it seems to solve the issue. Can somebody try this? Thanks everybody in the
community!
qx.Class.define("kreimer.MyImage2", {
extend : qx.ui.core.Widget,
/*
*****************************************************************************
CONSTRUCTOR
*****************************************************************************
*/
/**
*/
construct : function(source)
{
this.__contentElements = {};
this.base(arguments);
if (source) {
this.setSource(source);
}
},
/*
*****************************************************************************
PROPERTIES
*****************************************************************************
*/
{
/** The URL of the image. Setting it will possibly abort loading of
current image. */
{
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.
*/
{
check : "Boolean",
init : false,
themeable : true,
apply : "_applyScale"
},
// overridden
{
refine : true,
init : "image"
},
// overridden
{
refine : true,
init : false
},
// overridden
{
refine : true,
init : false
},
// overridden
{
refine : true,
init : false
},
// overridden
{
refine : true,
init : false
}
},
/*
*****************************************************************************
EVENTS
*****************************************************************************
*/
{
/**
* Fired if the image source can not be loaded. This event can only be
* fired for the first loading of an unmanaged resource (external
image).
*/
loadingFailed : "qx.event.type.Event",
/**
* Fired if the image has been loaded. This is even true for managed
* resources (images known by generator).
*/
loaded : "qx.event.type.Event",
/** Fired when the pending request has been aborted. */
aborted : "qx.event.type.Event"
},
{
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
},
/*
*****************************************************************************
MEMBERS
*****************************************************************************
*/
{
__width : null,
__height : null,
__mode : null,
__contentElements : null,
__currentContentElement : null,
__wrapper : null,
__requestId : 0,
//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
};
},
// 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, old)
{
// abort loading current image
if (old) {
if (qx.io.ImageLoader.isLoading(old)) {
qx.io.ImageLoader.abort(old);
}
}
this._styleSource();
},
// property apply
_applyScale : function(value) {
this._styleSource();
},
/**
* Remembers the mode to keep track which contentElement is currently in
use.
*/
__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
*
*/
__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 AliasManager = qx.util.AliasManager.getInstance();
var ResourceManager = qx.util.ResourceManager.getInstance();
var source = AliasManager.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.__getContentElement();
// Detect if the image registry knows this image
if (qx.util.ResourceManager.getInstance().has(source)) {
var highResolutionSource = this._findHighResolutionSource(source);
if (highResolutionSource) {
var imageWidth = ResourceManager.getImageHeight(source);
var imageHeight = ResourceManager.getImageWidth(source);
this.setWidth(imageWidth);
this.setHeight(imageHeight);
// set backgroud size on current element (div or img)
var backgroundSize = imageWidth + "px, " + imageHeight + "px";
this.__currentContentElement.setStyle("background-size",
backgroundSize);
this.setSource(highResolutionSource);
source = highResolutionSource;
}
this.__setManagedImage(contentEl, source);
this.__fireLoadEvent();
} else if (qx.io.ImageLoader.isLoaded(source)) {
this.__setUnmanagedImage(contentEl, source);
this.__fireLoadEvent();
} else {
this.__loadUnmanagedImage(contentEl, source);
}
},
/**
* Helper function, which fires <code>loaded</code> event
asynchronously.
* It emulates native <code>loaded</code> event of an image object. This
* helper will be called, if you try to load a managed image or an
* previously loaded unmanaged image.
*/
__fireLoadEvent : function()
{
this.__requestId++;
qx.bom.AnimationFrame.request(function(rId){
// prevent firing of the event if source changed in the meantime
if (rId === this.__requestId) {
this.fireEvent("loaded");
} else {
this.fireEvent("aborted");
}
}.bind(this, this.__requestId));
},
/**
* Returns the content element.
*/
__getContentElement : function()
{
var contentEl = this.__currentContentElement;
if (this.__wrapper) {
contentEl = contentEl.getChild(0);
}
return contentEl;
},
/**
* Checks if the current content element is capable to display the image
* with the current settings (scaling, alpha PNG)
*
*/
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
*
*/
__checkForContentElementReplacement : function(elementToAdd)
{
var currentContentElement = this.__currentContentElement;
if (currentContentElement != elementToAdd)
{
if (currentContentElement != null)
{
var pixel = "px";
var styles = {};
// kreimer
var currentStyles = currentContentElement.getAllStyles();
this.info("currentStyles" + currentStyles);
for(var prop in currentStyles) {
styles[prop] = currentStyles[prop];
}
//kreimer
// 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;
elementToAdd;
newEl.setStyles(styles, true);
newEl.setSelectable(this.getSelectable());
if (!currentContentElement.isVisible()) {
elementToAdd.hide();
}
if (!currentContentElement.isIncluded()) {
elementToAdd.exclude();
}
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.__getContentElement();
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();
// copy event listeners
var listeners = currentContentElement.getListeners() || [];
listeners.forEach(function(listenerData) {
elementToAdd.addListener(listenerData.type,
listenerData.handler, listenerData.self, listenerData.capture);
});
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
*
*/
__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
*
*/
__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
*
*/
__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.
*
*/
__setSource: function (el, source) {
if (el.getNodeName() == "div") {
// checks if a decorator already set.
// In this case we have to merge background styles
var decorator =
qx.theme.manager.Decoration.getInstance().resolve(this.getDecorator());
if (decorator) {
var hasGradient = (decorator.getStartColor() &&
decorator.getEndColor());
var hasBackground = decorator.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 decoratorStyle = decorator.getStyles(true);
var combinedStyles = {
"backgroundImage": attr.style.backgroundImage,
"backgroundPosition": (attr.style.backgroundPosition || "0
0"),
"backgroundRepeat": (attr.style.backgroundRepeat ||
"no-repeat")
};
if (hasBackground) {
combinedStyles["backgroundPosition"] += "," +
decoratorStyle["background-position"] || "0 0";
combinedStyles["backgroundRepeat"] += ", " +
decorator.getBackgroundRepeat();
}
if (hasGradient) {
combinedStyles["backgroundPosition"] += ", 0 0";
combinedStyles["backgroundRepeat"] += ", no-repeat";
}
combinedStyles["backgroundImage"] += "," +
decoratorStyle["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);
},
/**
* Detects whether there is a high-resolution image available.
* A high-resolution image is assumed to have the same file name as
* the parameter source, but with a pixelRatio identifier before the
file
*
*/
_findHighResolutionSource: function(lowResImgSrc) {
var pixelRatioCandidates = ["3", "2", "1.5"];
// Calculate the optimal ratio, based on the rem scale factor of the
application and the device pixel ratio.
var factor =
parseFloat(qx.bom.client.Device.getDevicePixelRatio().toFixed(2));
if (factor <= 1) {
return false;
}
var i = pixelRatioCandidates.length;
while (i > 0 && factor > pixelRatioCandidates[--i]) {}
var hiResImgSrc;
var k;
// Search for best img with a higher resolution.
for (k = i; k >= 0; k--) {
hiResImgSrc = this._getHighResolutionSource(lowResImgSrc,
pixelRatioCandidates[k]);
if (hiResImgSrc) {
return hiResImgSrc;
}
}
// Search for best img with a lower resolution.
for (k = i + 1; k < pixelRatioCandidates.length; k++) {
hiResImgSrc = this._getHighResolutionSource(lowResImgSrc,
pixelRatioCandidates[k]);
if (hiResImgSrc) {
return hiResImgSrc;
}
}
return null;
},
/**
* Returns the source name for the high-resolution image based on the
passed
* parameters.
image.
could be found.
*/
_getHighResolutionSource : function(source, pixelRatio) {
var fileExtIndex = source.lastIndexOf('.');
if (fileExtIndex > -1) {
var candidate = source.slice(0, fileExtIndex) + pixelRatioIdentifier
+ source.slice(fileExtIndex);
if(qx.util.ResourceManager.getInstance().has(candidate)) {
return candidate;
}
}
return null;
},
/**
* Event handler fired after the preloader has finished loading the icon
*
*/
__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())) {
this.fireEvent("aborted");
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) {
this.fireEvent("aborted");
return;
} else {
this.fireEvent("loaded");
}
// Update image
this.__setUnmanagedImage(this.__getContentElement(), source);
},
/**
* Updates the content hint when the image size has been changed
*
*/
__updateContentHint : function(width, height)
{
// Compare with old sizes and relayout if necessary
if (width !== this.__width || height !== this.__height)
{
this.__width = width;
this.__height = height;
qx.ui.core.queue.Layout.add(this);
}
}
},
/*
*****************************************************************************
DESTRUCTOR
*****************************************************************************
*/
destruct : function() {
for (var mode in this.__contentElements) {
if (this.__contentElements.hasOwnProperty(mode)) {
this.__contentElements[mode].setAttribute("$$widget", null, true);
}
}
delete this.__currentContentElement;
if (this.__wrapper) {
delete this.__wrapper;
}
this._disposeMap("__contentElements");
}
});
var btn = new qx.ui.form.CheckBox("Image#scale").set({ value : true});
var setSrcBtn = new qx.ui.form.Button("setGreen");
setSrcBtn.addListener("execute", function(e) {
img.setBackgroundColor("green");
}, this);
var img = new
kreimer.MyImage2("http://support.showreelfinder.com/hc/en-us/article_attachments/201500876/reset.png").set({
cursor : "pointer",
backgroundColor : "orange",
height : 64,
width : 64,
scale : true
});
var doc = this.getRoot();
doc.add(btn, { left: 10, top: 10 });
doc.add(setSrcBtn, { left: 150, top: 10 });
doc.add(img, { left: 10, top: 50 });
btn.bind("value", img, "scale");
--
View this message in context: http://qooxdoo.678.n2.nabble.com/cursor-on-images-with-scale-true-tp7588002p7588101.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=272487151&iu=/4140
_______________________________________________
qooxdoo-devel mailing list
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel
kreimer
2016-02-24 12:30:09 UTC
Permalink
Hello,

done! here it is: https://github.com/qooxdoo/qooxdoo/pull/176

Thanks!



--
View this message in context: http://qooxdoo.678.n2.nabble.com/cursor-on-images-with-scale-true-tp7588002p7588104.html
Sent from the qooxdoo mailing list archive at Nabble.com.
kreimer
2016-02-24 12:53:01 UTC
Permalink
Added some check on getAllStyles() being not null.



--
View this message in context: http://qooxdoo.678.n2.nabble.com/cursor-on-images-with-scale-true-tp7588002p7588105.html
Sent from the qooxdoo mailing list archive at Nabble.com.
John Spackman
2016-02-24 13:09:12 UTC
Permalink
Brill, thanks Bernardo
Post by kreimer
Added some check on getAllStyles() being not null.
--
View this message in context: http://qooxdoo.678.n2.nabble.com/cursor-on-images-with-scale-true-tp7588002p7588105.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=272487151&iu=/4140
_______________________________________________
qooxdoo-devel mailing list
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel
Loading...