Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Brush fixes and enhancements #3683

Open
wants to merge 46 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
5991ed3
Merge pull request #1 from rstudio/master
mjakubczak May 12, 2020
ec6e09e
Set plot brush programmatically
mjakubczak May 12, 2020
d2fe826
JS and Roxygen build changes
mjakubczak May 12, 2020
bcd9470
Merge branch 'mjak' into dev
dvg-p4 Jun 7, 2022
4f6232b
specify where submodule is supposed to point
dvg-p4 Jun 8, 2022
6872fe7
Bring updateDiv() calls inside of boundsCss()
dvg-p4 Jun 8, 2022
c507125
Works if brush_id is unique
dvg-p4 Jun 8, 2022
da03d0d
Make better changes to createBrush
dvg-p4 Jun 9, 2022
5f88950
Handle invalid panelIdx inputs
dvg-p4 Jun 9, 2022
7021f9a
Remove spurious 'resize' handlers
dvg-p4 Jun 9, 2022
e15eb02
Created user-friendly wrapper functions updateBrushCoords() and reset…
dvg-p4 Jun 9, 2022
c39cdeb
Move rounding to just before data send to fix
dvg-p4 Jun 10, 2022
b46478c
Remove console.log's
dvg-p4 Jun 10, 2022
f2e8352
Stop clipping things that ask not to be clipped
dvg-p4 Jun 10, 2022
f0ccc22
"Prevented spurious inputs from date inputs"
dvg-p4 Jun 13, 2022
4e5ae89
Bump alpha sub-version number
dvg-p4 Jun 13, 2022
cebc678
Fix linked documentation
dvg-p4 Jun 14, 2022
285670e
Fully commit to nonstandard version number
dvg-p4 Jun 14, 2022
0e9a2d5
Merge branch 'main' into dev
dvg-p4 Jun 14, 2022
41675aa
propogate nonstandard version number
dvg-p4 Jun 14, 2022
5218a5e
Round based on range to avoid spurious values near zero
dvg-p4 Jun 20, 2022
ab4d283
Bump version number
dvg-p4 Jun 21, 2022
3d4f166
Merge branch 'fix-throttle' into dev
dvg-p4 Jul 1, 2022
9032335
made brush/click/hpver handlers persist
dvg-p4 Jul 1, 2022
2ebc4cc
Merge final throttle fixes from 'main' into 'dev'
dvg-p4 Jul 6, 2022
5a508d2
Fixed errors when brush reset mid-drag
dvg-p4 Jul 6, 2022
a74bd17
Network debugging
dvg-p4 Jul 6, 2022
e3f5a46
only remove handlers when necessary
dvg-p4 Jul 6, 2022
9269967
Resize brush with cached plots and recover from rendering errors
dvg-p4 Jul 7, 2022
bcf565c
Removed console.log's and bump version no.
dvg-p4 Jul 7, 2022
0d8a5da
Remove changes to dates
dvg-p4 Aug 10, 2022
a437664
Merge branch 'main' into brush-fixes-and-enhancements
dvg-p4 Aug 12, 2022
90326f7
Updated pkgdown.yml with new functions
dvg-p4 Aug 12, 2022
85707d1
Sync package version (GitHub Actions)
dvg-p4 Aug 12, 2022
98c3128
Started editing NEWS.md; I need to look through the issue backlog to …
dvg-p4 Aug 12, 2022
b82ec8d
Send *debounced* brush coordinates when image resizes
dvg-p4 Aug 12, 2022
ec1561c
Cleaned up comments
dvg-p4 Aug 13, 2022
b5cdbae
typo fix
dvg-p4 Aug 13, 2022
ea7845c
Fixed comment
dvg-p4 Aug 13, 2022
8b97aed
Merge branch 'main' into brush-fixes-and-enhancements
dvg-p4 Sep 9, 2022
23edc46
Bugfix: don't set brush to null while resolving updateBrushCoords
dvg-p4 Dec 12, 2022
b1a8c75
Merge branch 'main'
dvg-p4 Mar 14, 2023
c74959b
Merge branch 'main'
dvg-p4 Mar 14, 2023
919a175
Merge branch 'main' into brush-fixes-and-enhancements
dvg-p4 Apr 4, 2023
5834899
Merge branch 'main' into brush-fixes-and-enhancements
dvg-p4 Sep 30, 2024
0378853
Merge branch 'main' into brush-fixes-and-enhancements
dvg-p4 Sep 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[submodule "tests/testthat/apps"]
url = https://github.com/rstudio/shiny-test-apps.git
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ export(renderText)
export(renderUI)
export(repeatable)
export(req)
export(resetBrush)
export(resourcePaths)
export(restoreInput)
export(runApp)
Expand Down Expand Up @@ -301,6 +302,7 @@ export(titlePanel)
export(uiOutput)
export(updateActionButton)
export(updateActionLink)
export(updateBrushCoords)
export(updateCheckboxGroupInput)
export(updateCheckboxInput)
export(updateDateInput)
Expand Down
10 changes: 10 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
# shiny (development version)

### Breaking changes

* Closed #2866: `session$resetBrush()` now properly namespaces the brush ID automatically within a module. This breaks previous workarounds, which will now result in a doubly-namespaced brush ID. This function has also been supplemented with the slightly more user-friendly `shiny::resetBrush()`; see details below.

## New features and improvements

* Small improvements to the default pulse busy indicator to better blend with any background. It's also now slightly smaller by default. (#4122)

* When spinners and the pulse busy indicators are enabled, Shiny now shows the pulse indicator when dynamic UI elements are recalculating if no other spinners are present in the app. (#4137)

* Added `resetBrush()` function for a slightly more user-friendly alternative to `session$resetBrush()` with syntax similar to the `update*()` functions.

* Added `updateBrushCoords()` function to programatically update the area brushed on a plot, similar to other `update*()` functions. Tested primarily with `ggplot2`, but may work with reduced functionality with base graphics and images. See `help(updateBrushCoords, shiny)` for more details. (#1456)

## Bug fixes

* Fixed a bug in `conditionalPanel()` that would cause the panel to repeatedly show/hide itself when the provided condition was not boolean. (@kamilzyla, #4127)
Expand All @@ -14,6 +22,8 @@

* `dateInput` and `dateRangeInput` no longer send immediate updates to the server when the user is typing a date input. Instead, it waits until the user presses Enter or clicks out of the field to send the update, avoiding spurious and incorrect date values. Note that an update is still sent immediately when the field is cleared. (#3664)

* Overhauled brush code so that brush handlers (as well as click/hover handlers) persist when a plot is redrawn, and consistently move any drawn brushes to the correct location if the plot is resized. This fixes an issue where a plot that's redrawn in response to a brush would circumvent the debouncer and sometimes update twice each time the brush was moved--or worse, constantly re-update itself as the brush was being dragged. This also fixes a frustrating-to-replicate bug where the rectangle drawn on the plot could become duplicated and desynced from the coordinates sent to the server with a series of rapid user inputs. (#2344, #1642)

# shiny 1.9.1

## Bug fixes
Expand Down
1 change: 1 addition & 0 deletions R/mock-session.R
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ makeExtraMethods <- function() {
# session$getDataObj() to access in a test expression.
"registerDataObj",
"reload",
"setBrush",
"resetBrush",
"sendBinaryMessage",
"sendChangeTabVisibility",
Expand Down
27 changes: 25 additions & 2 deletions R/shiny.R
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,15 @@ workerId <- local({
#' The `user`'s relevant group information. Useful for determining what
#' privileges the user should or shouldn't have.
#' }
#' \item{setBrush(brushId, coords, panel, outputId)}{
#' Sets the coordinates of a brush. This should generally be called
#' through the friendlier [updateBrushCoords()] wrapper; see the
#' documentation of that function for details on the parameters.
#' }
#' \item{resetBrush(brushId)}{
#' Resets/clears the brush with the given `brushId`, if it exists on
#' any `imageOutput` or `plotOutput` in the app.
#' Resets/clears all brushes with the given `brushId`, if they exists on
#' any `imageOutput` or `plotOutput` in the app. This can now be called
#' through the friendlier [resetBrush()] wrapper.
#' }
#' \item{sendCustomMessage(type, message)}{
#' Sends a custom message to the web page. `type` must be a
Expand Down Expand Up @@ -819,6 +825,12 @@ ShinySession <- R6Class(
sendInputMessage = function(inputId, message) {
.subset2(self, "sendInputMessage")(ns(inputId), message)
},
setBrush = function(brushId, coords, panel, outputId) {
.subset2(self, "setBrush")(ns(brushId), coords, panel, outputId)
},
resetBrush = function(brushId) {
.subset2(self, "resetBrush")(ns(brushId))
},
registerDataObj = function(name, data, filterFunc) {
.subset2(self, "registerDataObj")(ns(name), data, filterFunc)
},
Expand Down Expand Up @@ -1892,6 +1904,17 @@ ShinySession <- R6Class(
private$sendMessage(updateQueryString = list(
queryString = queryString, mode = mode))
},
setBrush = function(brushId, coords, panel, outputId) {
# input validity checking is done in the updateBrushCoords() wrapper
private$sendMessage(
setBrush = list(
brushId = brushId,
coords = coords,
panel = panel,
outputId = outputId
)
)
},
resetBrush = function(brushId) {
private$sendMessage(
resetBrush = list(
Expand Down
75 changes: 75 additions & 0 deletions R/update-input.R
Original file line number Diff line number Diff line change
Expand Up @@ -860,3 +860,78 @@ selectizeJSON <- function(data, req) {
res <- toJSON(columnToRowData(data))
httpResponse(200, 'application/json', enc2utf8(res))
}

#' Change the region selected by a brush on the client
#'
#' Sends a message to the client to set the region selected by a particular
#' plot/image brush to the specified coordinates.
#'
#' Sets the brush with the given `brushId`, if it exists on an output with
#' the given `outputId` (or, if none is specified, any `imageOutput` or
#' `plotOutput` in the app). The `coords` should be a
#' list with `xmin`, `xmax`, `ymin`, `ymax` single-element numerics that
#' specify the desired brush position (in the scale of the plot). `panel` should
#' be a single-element integer that defines the panel that should be brushed
#' for multi-faceted plots.
#'
#' If the direction of the brush was set to "x" in `brushOpts()`, then `ymin`
#' and `ymax` will be ignored, but must still be supplied, and vice versa.
#'
#' `outputId` need not be provided if the `brushId` is unique. However, if
#' multiple plot or image outputs have brushes with the same id, this must
#' be provided to specify which one should be brushed (since no more than one
#' may have an active brush at one time).
#'
#' Since brushes are not standard bound inputs, the update message does not
#' go onto the input message queue and may be sent immediately.
#'
#' @param session The `session` object passed to function given to
#' `shinyServer`. Default is `getDefaultReactiveDomain()`.
#' @param brushId The id of a brush given to `plotOutput` or `imageOutput`.
#' @param coords A list containing `xmin`, `xmax`, `ymin`, `ymax` numerics.
#' @param panel The numeric index of the panel to brush. Indices start from 0,
#' which is the default, and the only valid value for single-panel plots.
#' @param outputId The id of the `plotOutput` or `imageOutput` that contains
#' the brush to update. Can be NULL (default) if the brushId is unique.
#'
#' @seealso [brushOpts()]
#'
#' @export
updateBrushCoords <- function(session = getDefaultReactiveDomain(), brushId, coords, panel = 0, outputId = NULL) {
validate_session_object(session)
if (!all(c("xmin", "xmax", "ymin", "ymax") %in% names(coords))){
stop("coords must have xmin, xmax, ymin and ymax fields")
}
if (!is.numeric(coords$xmin) ||
!is.numeric(coords$xmax) ||
!is.numeric(coords$ymin) ||
!is.numeric(coords$ymax)) {
stop("all coordinates must be numeric")
}
if (!is.numeric(panel)) {
stop("panel must be numeric")
}

session$setBrush(brushId, coords, panel, outputId)
}

#' Resets/clears a brush (or brushes) on the client
#'
#' Sends a message to the client to reset all brushes with the given id.
#'
#' Multiple brushes from different plot or image outputs may share the
#' same id, but only one may have an active selection at a time. This
#' function will reset whichever one is active (if any).
#'
#' Since brushes are not standard bound inputs, the reset message does not
#' go onto the input message queue and may be sent immediately.
#'
#' @param session The `session` object passed to function given to
#' `shinyServer`. Default is `getDefaultReactiveDomain()`.
#' @param brushId The id of a brush given to `plotOutput` or `imageOutput`.
#'
#' @export
resetBrush <- function(session = getDefaultReactiveDomain(), brushId) {
validate_session_object(session)
session$resetBrush(brushId)
}
Loading
Loading