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 chainImproved 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 Ts 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
afteranimation function that allows a delay before executing anAnimationPlan. - Made
Scenea public type since it is part of the public API forTheme - New builders for creating Sliders for
CharandMeasure<T> - New
incrementanddecrementmethods for sliders - New methods for incrementing/decrementing start/end for
RangeValueSlider - Renamed
SpinnertoSpinButton(and related classes) and deprecated all old uses. - New
SpinButtonRolefor accessibility. ThemePickernow allows customization of accessible value labels via newvalueAccessibilityLabelerproperty.ListItemRolenow has aselectedstate.ListItem(BasicTheme) now keeps this value up-to-date.- New
circumferenceextension forCircle - New helper for calculating the interior angle between two
Vector3Dinstances. - New form methods for creating
SpinButtonform controls from aListof 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
afterfunction. - 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