Features
Animation Chaining
Animations can now be chained within an animation block using the new then
method. This makes it easier to have sequential animations and avoids the need to explicitly track secondary animations for cancellation, since these are tied to their "parent" animation.
val animation = animate {
0f to 1f using (tweenFloat(easing, duration)) { // (1)
// ...
} then {
0f to 1f using (after(delay, tweenFloat(easing, duration))) { // (2)
} then { // (3)
// ...
}
} then { // (4)
// ...
}
}
animation.completed += { /* ... */ } // applies to entire chain
animation.pause () // applies to entire chain
animation.cancel() // applies to entire chain
Improved Accessibility
Desktop Support
Doodle's web apps have had accessibility support for some time. Now those capabilities are available for desktop apps as well. You simply include the AccessibilityModule in your app and follow the guidelines of how to add roles, labels, etc. to your Views.
SpinButton Accessibility
SpinButtons now use the new SpinButtonRole that allows assistive tools to better read them. This role exposes the currently selected value based on a new valueAccessibilityLabeler
function that converts the value to a String
.
Improved Sliders
Arbitrary Types
Sliders can now represent values of any Comparable
type T
between two start
and end
values. This is possible for T
s that have some interpolation between a start
and end
based on some value between 0
and 1
. This is done via a new TypeConverter<T>
that defines the interpolation (and its inverse).
This means you can now create sliders for numeric types like Measure<T>
directly and their value
will by of the right type.
val charSlider = Slider('A' .. 'Z')
val velocitySlider = Slider(10 * meters / seconds .. 100 * miles / hours)
You can also create Sliders for any type T
, as long as it is Comparable
and you can create an Interpolator
for it.
fun <T: Comparable<T>> customSlider(model: ConfinedValueModel<T>, interpolator: Interpolator<T>) {
val slider: Slider<T> = Slider(model, interpolator = interpolator)
}
These, more flexible Sliders can also be used in forms as expected.
Form {this(
+ slider('A' .. 'Z'),
+ slider(10 * meters/seconds .. 10 * miles/hours),
+ slider(model, interpolator = interpolator),
onInvalid = {}
) { _: Char, _: Measure<Velocity>, _: T ->
}}
Non-linearity
Sliders are linear by default, which means a change in their position translates to a linear change in their value. There are cases however, when it makes sense to have a slider's value change in a non-linear way. You can do this by providing a function that maps values between the slider's input and output spaces. These values are all within the [0-1] domain, and work very similarly to easing functions used for animations. The big difference is they have two forms: f(x) and f^-1(x).
import io.nacular.doodle.controls.range.InvertibleFunction
import io.nacular.doodle.controls.range.Slider
import kotlin.math.log
import kotlin.math.pow
/**
* Logarithmic function and inverse https://www.desmos.com/calculator/qq59ey0bub
*/
private object LogFunction: InvertibleFunction {
override fun invoke (value: Float) = log((10f - 1) * value + 1, 10f)
override fun inverse(value: Float) = (10f.pow(value) - 1)/(10 - 1)
}
val logarithmicSlider = Slider(0.0 .. 1.0, function = LogFunction)
APIs
-
General
- New
after
animation function that allows a delay before executing anAnimationPlan
. - Made
Scene
a public type since it is part of the public API forTheme
- New builders for creating Sliders for
Char
andMeasure<T>
- New
increment
anddecrement
methods for sliders - New methods for incrementing/decrementing start/end for
RangeValueSlider
- Renamed
Spinner
toSpinButton
(and related classes) and deprecated all old uses. - New
SpinButtonRole
for accessibility. ThemePicker
now allows customization of accessible value labels via newvalueAccessibilityLabeler
property.ListItemRole
now has aselected
state.ListItem
(BasicTheme) now keeps this value up-to-date.- New
circumference
extension forCircle
- New helper for calculating the interior angle between two
Vector3D
instances. - New form methods for creating
SpinButton
form controls from aList
of values orIntProgression
. - New functions for creating ease[In/Out]Bounce EasingFunctions with an
initialBounceFraction
:easeIn(0.15f)
. - New convenience methods for working with PathBuilders using x,y instead of Point.
- New
-
Deprecations
- All animation functions that take a delay, since there is a new
after
function. - Types and functions related to
Spinner
, which was renamed toSpinButton
. - Types and functions related to
Dropdown
, which was renamed toSelectBox
.
- All animation functions that take a delay, since there is a new
Fixes | Improvements
-
General
- Issue where item could be stuck in render loop if it requires a layout, but cannot render b/c it is not recursively visible (it and all ancestors visible).
-
Browser
- Fixed issue with Display pointer exit not being properly handled.
- Work-around for Safari giving incorrect clientX/Y values when the browser window is zoomed, which broke pointer location.
- Fixed edge case where old rendered vectors aren't cleaned up if a sub-frame happens where one wasn't before.
- Fixed bug in reading items from DataTransferItemList that broke file drag-drop
Versions
- Kotlin -> 1.9.23
- Kover -> 0.8.1
- Dokka -> 1.9.20