From dbf176b74cbfffd30d7e7d03e870fbdb9967c98f Mon Sep 17 00:00:00 2001 From: Thomas Lin Pedersen Date: Tue, 23 Apr 2024 10:17:17 +0200 Subject: [PATCH] Fix #4 as well as do precalc for makeContext/makeContent if the widths are absolute/NA --- NAMESPACE | 2 ++ R/grob.R | 34 +++++++++++++++++++++++++++++++++- man/marquee_parse.Rd | 37 ++++++++++++++++++++++++++++++++++--- 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 5b15ce0..3adf6b8 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -8,8 +8,10 @@ S3method(format,marquee_style) S3method(format,marquee_style_set) S3method(heightDetails,textboxgrob) S3method(makeContent,marquee) +S3method(makeContent,marquee_precalculated) S3method(makeContent,svg_grob) S3method(makeContext,marquee) +S3method(makeContext,marquee_precalculated) S3method(print,marquee_box) S3method(print,marquee_em) S3method(print,marquee_relative) diff --git a/R/grob.R b/R/grob.R index c767a79..642502a 100644 --- a/R/grob.R +++ b/R/grob.R @@ -191,12 +191,18 @@ marquee_grob <- function(text, style, x = 0, y = 1, width = NULL, default.units cli::cli_abort("{.arg vjust} must either be numeric or a character vector") } - gTree( + grob <- gTree( text = parsed, bullets = bullets, images = images, x = rep_along(text, x), y = rep_along(text, y), width = rep_along(text, width), hjust = rep_along(text, hjust), vjust = rep_along(text, vjust), angle = rep_along(text, angle), vp = vp, name = name, cl = "marquee" ) + # Check if we can do all calculations upfront + if (all(is.na(grob$width) | unitType(absolute.size(grob$width)) != "null")) { + grob <- makeContent.marquee(makeContext.marquee(grob)) + class(grob) <- c("marquee_precalculated", class(grob)) + } + grob } #' @export @@ -324,6 +330,26 @@ makeContext.marquee <- function(x) { space_before = 0, space_after = 0 ) + + # If any widths are not defined we grab the text widths from the shaping and start again + if (anyNA(widths)) { + added_width <- rowSums(x$text[block_starts, c("padding_left", "padding_right", "margin_left", "margin_right")]) + ## We go back from the most indented to the least and compound the widths + for (i in rev(seq_len(max(block_indent)))) { + blocks <- which(block_indent == i) + widths[blocks] <- vapply(blocks, function(j) { + k <- which(block_starts > block_starts[j] & block_starts <= x$text$ends[block_starts[j]]) + max(shape$metrics$width[j], widths[j], widths[k] + added_width[k], na.rm = TRUE) + }, numeric(1)) + } + if (anyNA(widths)) { + cli::cli_abort("Could not resolve width of text") + } + x$width <- unit(widths[block_indent == 1], "bigpts") + ## Restart evaluation + return(makeContext.marquee(x)) + } + # Inherit color and id from parsed text shape$shape$col <- x$text$color[shape$shape$string_id] shape$shape$id <- x$text$id[shape$shape$string_id] @@ -618,6 +644,9 @@ makeContext.marquee <- function(x) { x } +#' @export +makeContext.marquee_precalculated <- function(x) x + #' @export heightDetails.textboxgrob <- function(x) { x$full_height @@ -784,3 +813,6 @@ makeContent.marquee <- function(x) { setChildren(x, inject(gList(!!!grobs))) } + +#' @export +makeContent.marquee_precalculated <- function(x) x diff --git a/man/marquee_parse.Rd b/man/marquee_parse.Rd index 0370eba..f48d8fe 100644 --- a/man/marquee_parse.Rd +++ b/man/marquee_parse.Rd @@ -69,6 +69,9 @@ inside this span is rendered verbatim \code{u} is text that should be underlined \code{del} is text that should have strikethrough + +\emph{custom spans} is a marquee specific extension to the syntax that allows you +to make up tags on the fly. See the section on marquee syntax for more. } \section{marquee syntax}{ @@ -95,19 +98,47 @@ HTML tags are ignored, i.e. they are rendered verbatim. This is not that different from classic markdown rendering except that people often convert markdown to HTML where these tags suddenly have meaning. They do not carry any special significance when rendered with marquee + +\strong{Custom tags} + +While markdown provides most of what is necessary for standard text markup, +there are situations, especially in visualisation, where we need something +more. Often users reach for inline HTML spans for that, but since HTML is +fully ignored in marquee this is not an option. Further, adding in HTML +decreases readability of the unformatted text a lot. + +With marquee you can create a custom span using the \verb{\{.tag \}} +syntax, e.g. \verb{\{.sm small text\}} to wrap "small text" in the \code{sm} tag. You can +alternatively use \verb{\{#tag \}} for the same effect. The only +difference is that in the former syntax the \code{.} is stripped from the tag name, +whereas in the latter the \verb{#} remains part of the name. See the Styling +section for the primal use of the latter syntax. } \section{Styling}{ During parsing, each token is assigned a style based on the provided style set. The styling is cascading, but without the intricacies of CSS. A child -element inherits the styling of it's parent for the elements that are set to +element inherits the styling of it's parent for the options that are set to \code{NULL} in the style matching the child tag. Any style element that are \code{\link[=relative]{relative()}} are computed based on the value of the parent style element. \code{\link[=em]{em()}} elements are resolved based on the size element of the child style, and \code{\link[=rem]{rem()}} elements are resolved using the size element of the \code{body} style. +If a style is not provided for the tag, it fully inherits the style of it's +parent. + +\strong{Automatic coloring} +Recognizing that the primary use for custom tags may be to change the color +of some text, marquee provides a shortcut for this. If a style is not found +for the tag in the provided style set, marquee will check if the tag matches +a valid color (i.e. a string from \code{grDevices::colors()}, or a valid hex +string, e.g. \verb{#53f2a9}). If it is a valid color it will set this as the font +color of the style. This means that parsing "Color {.red this} red" +automatically sets the color of "this" to red, even if no style is provided +for the \code{red} tag. Likewise, parsing "Color {#00FF00 me} green" will +automatically set the color of "me" to #00FF00 (fully satuared green). +} -\strong{Additional parsing information} - +\section{Additional parsing information}{ Apart from splitting the text up into tokens, \code{marquee_parse()} also provides some additional information useful for rendering the output in the expected way. The \code{id} column refers the tokens back to the original input text, the