Nick
03/10/2023, 4:52 PMPopupManager
. Before, you could make custom popups by directly adding items to the Display
. This worked in many cases, but had a lot of limitations. For example, Views added this way would be affected by any Layout
on the Display
, which meant you couldn't control these Popups as easily. Also, new Views added to the Display
after a custom popup was shown could overlay the popup. So there was no way to guarantee that a popup remained the top-most View. The new PopupManager fixes these limitations and provides a simple API for showing popups.
Any View Can Be A Popup
The PopupManager's API works with View
, so you can make anything a popup. Simply do the following:
popupManager.show(myView) {
it.center eq parent.center
}
// ...
popupManager.hide(myView)
Views that are shown as popups will automatically become top-level (being removed from their existing parent if already displayed) and sit above all other existing Views. Hiding a popup removes it from the Display
, but it won't return the View to a previous parent if it had one.
Popup Layouts
Popups are shown with layout information directly. There are two ways to show a popup: relative to the Display
, or relative to another View
and the Display
. There is an API call for each:
Here, myView
is positioned with only a "reference" to the Display (parent).
popupManager.show(myView) {
it.center eq parent.center
}
But sometimes a popup needs to be positioned relative to another View. This shows how to place myView
so it tracks the bounds of someView
. The PopupManager will handle keeping the popup aligned with someView
.
// myView is positioned with only a "reference" to the Display (parent)
popupManager.show(myView, relativeTo = someView) { popup, anchor ->
<http://popup.top|popup.top> greaterEq 0 // Popup top-left always visible
popup.left greaterEq 0 // Popup top-left always visible
popup.width.preserve // don't shrink popup
popup.height.preserve // don't shrink popup
(popup.right lessEq parent.right ) .. Strong // stay in parent as long as doesn't compress popup
(popup.bottom lessEq parent.bottom) .. Strong // stay in parent as long as doesn't compress popup
(<http://popup.top|popup.top> eq anchor.bottom + 10) .. Medium // follow anchor, as long as stronger constraints not in conflict
(popup.centerY eq anchor.centerY ) .. Medium // follow anchor, as long as stronger constraints not in conflict
}
More Powerful Text Rendering
Text Alignment
There is a new TextAlignment
enum that controls how wrapped text is displayed within its margins. This is a replacement for HorizontalAlignment
, which currently uses Left
, Right
(instead of Start
, End
) and does not support Justify
. You can justify text by doing the following:
canvas.wrapped(
text,
at = Origin,
leftMargin = 0.0,
rightMargin = width,
alignment = TextAlignment.Justify,
fill = Black.paint
)
canvas.wrapped(
styledText,
at = Origin,
leftMargin = 0.0,
rightMargin = width,
alignment = TextAlignment.Justify,
)
Letter, Word, And Line Spacing
You can now control the way letters, words and lines are spaced when rendering text. Letter and words spacing can be provided to the text rendering methods on Canvas
using the new TextSpacing
class. This information can also be passed to TextMetrics
when measuring text. Label
also has support for both letter and word spacing.
Line spacing can also be specified whenever you deal with wrapped text.
APIs
• Rectangle.toPath
that allows specifying each corner radius
• Exposing insets
in simpleTextButtonRenderer
function
• repeat
and loop
animations can now have delays
• Canvas
can now render wrapped text with custom line-spacing
• Label
now has lineSpacing
property which controls how it displays wrapped text
• Animatable Properties are no longer restricted to use within Views
• New method to create Ellipse
and Circle
by inscribing within Rectangle
• New Circle.diameter
property
• New constructor for StyledText
that takes a String
and Style
Accessibility
• Browser
◦ Button
now sets accessibilityLabel
to its text
if no label is already provided
◦ HyperLink
with native behavior will now apply aria-label to the anchor tag using the HyperLink's text when the AccessibilityManager
is present
Fixes | Improvements
• General
◦ Bug with empty AnimationBlock when created during active animation
◦ Wrapped text not correctly aligned
◦ Bug in FilteredList
iterator.remove
◦ CommonLabelBehavior
no longer provides an x offset for wrapped text since the alignment is handled correctly by Canvas
◦ Edge case where layout can hang b/c of double imprecision
◦ Issue where CommonLabelBehavior
overrides Label.foregroundColor
on uninstall
◦ Bug where incorrect item could be removed from RenderManagerImpl.pendingLayout
◦ Bug where some View properties lost during behavior install
◦ Bug where some views not cleaned up by RenderManager
◦ Bug in pointer handling when Display
transformed
◦ ToggleButton
no longer relies on being displayed to listen to its model
◦ StyledText.text
now returns correct value
• Browser
◦ Misidentifying elements as native scroll panels led to incorrect pointer behavior
◦ Reuse instances for linear/radial gradient paints
◦ Reusing clipped images
◦ Hyperlinks now open in new tab
◦ Shadow render bug when using SVG
◦ Element sometimes not reused when drawing shadow
◦ Remove overflow on element w/ shadow
◦ Fixed wrapped StyledText
rendering
◦ Wrapped StyledText
now supports text decoration on previously unsupported cases
• Desktop
◦ Fixed SVG image file loading issue
◦ Fixed rendering of images with radius
◦ Issue where font families weren't being properly tracked and therefore incorrectly applied
◦ Incorrect paragraph width measurement