@qooxdoo/framework
Version:
The JS Framework for Coders
463 lines (394 loc) • 12.5 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2008 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Sebastian Werner (wpbasti)
* Andreas Ecker (ecker)
* Adrian Olaru (adrianolaru)
************************************************************************ */
/**
* The tooltip manager globally manages the tooltips of all widgets. It will
* display tooltips if the user hovers a widgets with a tooltip and hides all
* other tooltips.
*/
qx.Class.define("qx.ui.tooltip.Manager", {
type: "singleton",
extend: qx.core.Object,
/*
*****************************************************************************
CONSTRUCTOR
*****************************************************************************
*/
construct() {
super();
// Register events
qx.event.Registration.addListener(
document.body,
"pointerover",
this.__onPointerOverRoot,
this,
true
);
// Instantiate timers
this.__showTimer = new qx.event.Timer();
this.__showTimer.addListener("interval", this.__onShowInterval, this);
this.__hideTimer = new qx.event.Timer();
this.__hideTimer.addListener("interval", this.__onHideInterval, this);
// Init pointer position
this.__pointerPosition = { left: 0, top: 0 };
},
/*
*****************************************************************************
PROPERTIES
*****************************************************************************
*/
properties: {
/** Holds the current ToolTip instance */
current: {
check: "qx.ui.tooltip.ToolTip",
nullable: true,
apply: "_applyCurrent"
},
/** Show all invalid form fields tooltips . */
showInvalidToolTips: {
check: "Boolean",
init: true
},
/** Show all tooltips. */
showToolTips: {
check: "Boolean",
init: true
}
},
/*
*****************************************************************************
MEMBERS
*****************************************************************************
*/
members: {
__pointerPosition: null,
__hideTimer: null,
__showTimer: null,
__sharedToolTip: null,
__sharedErrorToolTip: null,
/**
* Get the shared tooltip, which is used to display the
* {@link qx.ui.core.Widget#toolTipText} and
* {@link qx.ui.core.Widget#toolTipIcon} properties of widgets.
* You can use this public shared instance to e.g. customize the
* look and feel.
*
* @return {qx.ui.tooltip.ToolTip} The shared tooltip
*/
getSharedTooltip() {
if (!this.__sharedToolTip) {
this.__sharedToolTip = new qx.ui.tooltip.ToolTip().set({
rich: true
});
}
return this.__sharedToolTip;
},
/**
* Get the shared tooltip, which is used to display the
* {@link qx.ui.core.Widget#toolTipText} and
* {@link qx.ui.core.Widget#toolTipIcon} properties of widgets.
* You can use this public shared instance to e.g. customize the
* look and feel of the validation tooltips like
* <code>getSharedErrorTooltip().getChildControl("atom").getChildControl("label").set({rich: true, wrap: true, width: 80})</code>
*
* @return {qx.ui.tooltip.ToolTip} The shared tooltip
*/
getSharedErrorTooltip() {
if (!this.__sharedErrorToolTip) {
this.__sharedErrorToolTip = new qx.ui.tooltip.ToolTip().set({
appearance: "tooltip-error",
rich: true
});
this.__sharedErrorToolTip.setLabel(""); // trigger label widget creation
this.__sharedErrorToolTip.syncAppearance();
}
return this.__sharedErrorToolTip;
},
/*
---------------------------------------------------------------------------
PROPERTY APPLY ROUTINES
---------------------------------------------------------------------------
*/
// property apply
_applyCurrent(value, old) {
// Return if the new tooltip is a child of the old one
if (old && qx.ui.core.Widget.contains(old, value)) {
return;
}
// If old tooltip existing, hide it and clear widget binding
if (old) {
if (!old.isDisposed()) {
old.exclude();
}
this.__showTimer.stop();
this.__hideTimer.stop();
}
var Registration = qx.event.Registration;
var el = document.body;
// If new tooltip is not null, set it up and start the timer
if (value) {
this.__showTimer.startWith(value.getShowTimeout());
// Register hide handler
Registration.addListener(
el,
"pointerout",
this.__onPointerOutRoot,
this,
true
);
Registration.addListener(
el,
"focusout",
this.__onFocusOutRoot,
this,
true
);
Registration.addListener(
el,
"pointermove",
this.__onPointerMoveRoot,
this,
true
);
} else {
// Deregister hide handler
Registration.removeListener(
el,
"pointerout",
this.__onPointerOutRoot,
this,
true
);
Registration.removeListener(
el,
"focusout",
this.__onFocusOutRoot,
this,
true
);
Registration.removeListener(
el,
"pointermove",
this.__onPointerMoveRoot,
this,
true
);
}
},
/*
---------------------------------------------------------------------------
TIMER EVENT HANDLER
---------------------------------------------------------------------------
*/
/**
* Event listener for the interval event of the show timer.
*
* @param e {qx.event.type.Event} Event object
*/
__onShowInterval(e) {
var current = this.getCurrent();
if (current && !current.isDisposed()) {
this.__hideTimer.startWith(current.getHideTimeout());
if (current.getPlaceMethod() == "widget") {
current.placeToWidget(current.getOpener());
} else {
current.placeToPoint(this.__pointerPosition);
}
current.show();
}
this.__showTimer.stop();
},
/**
* Event listener for the interval event of the hide timer.
*
* @param e {qx.event.type.Event} Event object
*/
__onHideInterval(e) {
var current = this.getCurrent();
if (current && !current.getAutoHide()) {
return;
}
if (current && !current.isDisposed()) {
current.exclude();
}
this.__hideTimer.stop();
this.resetCurrent();
},
/*
---------------------------------------------------------------------------
POINTER EVENT HANDLER
---------------------------------------------------------------------------
*/
/**
* Global pointer move event handler
*
* @param e {qx.event.type.Pointer} The move pointer event
*/
__onPointerMoveRoot(e) {
var pos = this.__pointerPosition;
pos.left = Math.round(e.getDocumentLeft());
pos.top = Math.round(e.getDocumentTop());
},
/**
* Searches for the tooltip of the target widget. If any tooltip instance
* is found this instance is bound to the target widget and the tooltip is
* set as {@link #current}
*
* @param e {qx.event.type.Pointer} pointerover event
*/
__onPointerOverRoot(e) {
var target = qx.ui.core.Widget.getWidgetByElement(e.getTarget());
// take first coordinates as backup if no move event will be fired (e.g. touch devices)
this.__onPointerMoveRoot(e);
this.showToolTip(target);
},
/**
* Explicitly show tooltip for particular form item.
*
* @param target {Object | null} widget to show tooltip for
*/
showToolTip(target) {
if (!target) {
return;
}
var tooltip, tooltipText, tooltipIcon, invalidMessage;
// Search first parent which has a tooltip
while (target != null) {
tooltip = target.getToolTip();
tooltipText = target.getToolTipText() || null;
tooltipIcon = target.getToolTipIcon() || null;
if (
qx.Class.hasInterface(target.constructor, qx.ui.form.IForm) &&
!target.isValid()
) {
invalidMessage = target.getInvalidMessage();
}
if (tooltip || tooltipText || tooltipIcon || invalidMessage) {
break;
}
target = target.getLayoutParent();
}
//do nothing if
if (
!target || //don't have a target
// tooltip is disabled and the value of showToolTipWhenDisabled is false
(!target.getEnabled() && !target.isShowToolTipWhenDisabled()) ||
//tooltip is blocked
target.isBlockToolTip() ||
//an invalid message isn't set and tooltips are disabled
(!invalidMessage && !this.getShowToolTips()) ||
//an invalid message is set and invalid tooltips are disabled
(invalidMessage && !this.getShowInvalidToolTips())
) {
return;
}
if (invalidMessage) {
tooltip = this.getSharedErrorTooltip().set({
label: invalidMessage
});
}
if (!tooltip) {
tooltip = this.getSharedTooltip().set({
label: tooltipText,
icon: tooltipIcon
});
}
this.setCurrent(tooltip);
tooltip.setOpener(target);
},
/**
* Resets the property {@link #current} if there was a
* tooltip and no new one is created.
*
* @param e {qx.event.type.Pointer} pointerout event
*/
__onPointerOutRoot(e) {
var target = qx.ui.core.Widget.getWidgetByElement(e.getTarget());
if (!target) {
return;
}
var related = qx.ui.core.Widget.getWidgetByElement(e.getRelatedTarget());
if (!related && e.getPointerType() == "mouse") {
return;
}
var tooltip = this.getCurrent();
// If there was a tooltip and
// - the destination target is the current tooltip
// or
// - the current tooltip contains the destination target
if (
tooltip &&
(related == tooltip || qx.ui.core.Widget.contains(tooltip, related))
) {
return;
}
// If the destination target exists and the target contains it
if (related && target && qx.ui.core.Widget.contains(target, related)) {
return;
}
if (tooltip && !tooltip.getAutoHide()) {
return;
}
// If there was a tooltip and there is no new one
if (tooltip && !related) {
this.setCurrent(null);
} else {
this.resetCurrent();
}
},
/*
---------------------------------------------------------------------------
FOCUS EVENT HANDLER
---------------------------------------------------------------------------
*/
/**
* Reset the property {@link #current} if the
* current tooltip is the tooltip of the target widget.
*
* @param e {qx.event.type.Focus} blur event
*/
__onFocusOutRoot(e) {
var target = qx.ui.core.Widget.getWidgetByElement(e.getTarget());
if (!target) {
return;
}
var tooltip = this.getCurrent();
if (tooltip && !tooltip.getAutoHide()) {
return;
}
// Only set to null if blurred widget is the
// one which has created the current tooltip
if (tooltip && tooltip == target.getToolTip()) {
this.setCurrent(null);
}
}
},
/*
*****************************************************************************
DESTRUCTOR
*****************************************************************************
*/
destruct() {
// Deregister events
qx.event.Registration.removeListener(
document.body,
"pointerover",
this.__onPointerOverRoot,
this,
true
);
// Dispose timers
this._disposeObjects("__showTimer", "__hideTimer", "__sharedToolTip");
this.__pointerPosition = null;
}
});