From 2ce45eab064b38c6f3f9775748217cd57ea38d73 Mon Sep 17 00:00:00 2001 From: Winston Chang Date: Mon, 11 May 2015 23:43:06 -0500 Subject: [PATCH] Hovering: add option to send NULL when mouse is outside --- R/image-interact-opts.R | 9 +++++++-- man/hoverOpts.Rd | 6 +++++- srcjs/output_binding_image.js | 34 ++++++++++++++++++++++++++++------ 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/R/image-interact-opts.R b/R/image-interact-opts.R index 53a6653ce..e6952a88a 100644 --- a/R/image-interact-opts.R +++ b/R/image-interact-opts.R @@ -65,9 +65,13 @@ dblclickOpts <- function(id = NULL, clip = TRUE, delay = 400) { #' @param clip Should the hover area be clipped to the plotting area? If FALSE, #' then the server will receive hover events even when the mouse is outside #' the plotting area, as long as it is still inside the image. +#' @param nullOutside If \code{FALSE} (the default), the value will stop +#' changing when the cursor exits the plotting area. If \code{TRUE}, the value +#' will be set to \code{NULL} when the mouse exits the plotting area. #' @export hoverOpts <- function(id = NULL, delay = 300, - delayType = c("debounce", "throttle"), clip = TRUE) { + delayType = c("debounce", "throttle"), clip = TRUE, + nullOutside = FALSE) { if (is.null(id)) stop("id must not be NULL") @@ -75,7 +79,8 @@ hoverOpts <- function(id = NULL, delay = 300, id = id, delay = delay, delayType = match.arg(delayType), - clip = clip + clip = clip, + nullOutside = nullOutside ) } diff --git a/man/hoverOpts.Rd b/man/hoverOpts.Rd index d97ace3f8..f8d923bf2 100644 --- a/man/hoverOpts.Rd +++ b/man/hoverOpts.Rd @@ -5,7 +5,7 @@ \title{Create an object representing hover options} \usage{ hoverOpts(id = NULL, delay = 300, delayType = c("debounce", "throttle"), - clip = TRUE) + clip = TRUE, nullOutside = FALSE) } \arguments{ \item{id}{Input value name. For example, if the value is \code{"plot_hover"}, @@ -23,6 +23,10 @@ while the cursor is moving, and wait until the cursor has been at rest for \item{clip}{Should the hover area be clipped to the plotting area? If FALSE, then the server will receive hover events even when the mouse is outside the plotting area, as long as it is still inside the image.} + +\item{nullOutside}{If \code{FALSE} (the default), the value will stop +changing when the cursor exits the plotting area. If \code{TRUE}, the value +will be set to \code{NULL} when the mouse exits the plotting area.} } \description{ This generates an object representing hovering options, to be passed as the diff --git a/srcjs/output_binding_image.js b/srcjs/output_binding_image.js index c67ade325..828582eb8 100644 --- a/srcjs/output_binding_image.js +++ b/srcjs/output_binding_image.js @@ -44,6 +44,7 @@ $.extend(imageOutputBinding, { hoverClip: OR($el.data('hover-clip'), true), hoverDelayType: OR($el.data('hover-delay-type'), 'debounce'), hoverDelay: OR($el.data('hover-delay'), 300), + hoverNullOutside: OR(strToBool($el.data('hover-null-outside')), false), brushId: $el.data('brush-id'), brushClip: OR(strToBool($el.data('brush-clip')), true), @@ -107,8 +108,10 @@ $.extend(imageOutputBinding, { if (opts.hoverId) { var hoverHandler = imageutils.createHoverHandler(opts.hoverId, - opts.hoverDelay, opts.hoverDelayType, opts.hoverClip, opts.coordmap); + opts.hoverDelay, opts.hoverDelayType, opts.hoverClip, + opts.hoverNullOutside, opts.coordmap); $el.on('mousemove.image_output', hoverHandler.mousemove); + $el.on('mouseout.image_output', hoverHandler.mouseout); $img.on('remove', hoverHandler.onRemoveImg); } @@ -396,8 +399,9 @@ imageutils.initCoordmap = function($el, coordmap) { // Returns a function that sends mouse coordinates, scaled to data space. // If that function is passed a null event, it will send null. - coordmap.mouseCoordinateSender = function(inputId, clip) { - clip = clip || true; + coordmap.mouseCoordinateSender = function(inputId, clip, nullOutside) { + if (clip === undefined) clip = true; + if (nullOutside === undefined) nullOutside = false; return function(e) { if (e === null) { @@ -406,7 +410,15 @@ imageutils.initCoordmap = function($el, coordmap) { } var offset = coordmap.mouseOffset(e); - // Ignore events outside of plotting region + // If outside of plotting region + if (!coordmap.isInPanel(offset)) { + if (nullOutside) { + exports.onInputChange(inputId, null); + return; + } + if (clip) + return; + } if (clip && !coordmap.isInPanel(offset)) return; var panel = coordmap.getPanel(offset); @@ -550,8 +562,10 @@ imageutils.createClickHandler = function(inputId, clip, coordmap) { }; -imageutils.createHoverHandler = function(inputId, delay, delayType, clip, coordmap) { - var sendHoverInfo = coordmap.mouseCoordinateSender(inputId, clip); +imageutils.createHoverHandler = function(inputId, delay, delayType, clip, + nullOutside, coordmap) +{ + var sendHoverInfo = coordmap.mouseCoordinateSender(inputId, clip, nullOutside); var hoverInfoSender; if (delayType === 'throttle') @@ -559,8 +573,16 @@ imageutils.createHoverHandler = function(inputId, delay, delayType, clip, coordm else hoverInfoSender = new Debouncer(null, sendHoverInfo, delay); + // What to do when mouse exits the image + var mouseout; + if (nullOutside) + mouseout = function() { hoverInfoSender.normalCall(null); }; + else + mouseout = function() {}; + return { mousemove: function(e) { hoverInfoSender.normalCall(e); }, + mouseout: mouseout, onRemoveImg: function() { hoverInfoSender.immediateCall(null); } }; };