Cherrio LLC
08/20/2023, 10:11 AMVerticalDynamicList
in the desktop app. I’m thinking maybe it’s the JRE version. But I’ve tried every Java runtime version I have installed on my pc, same thing.Nick
08/25/2023, 3:00 PMCarousel
control is a View
that contains a lazy list of items generated from a ListModel
. It delegates all decisions about what items to show to a Presenter
obtained from its CarouselBehavior
. The Carousel
only displays items the Presenter
requests as it moves through frames. This means it can scale to very large datasets as long as the Presenter
only shows items that are visible.
`Carousel`s can have a very wide range of layouts and behaviors. This flexibility is achieved by giving `Presenter`s full control over which items (including supplemental ones not based on the data model) are shown at any point, how opaque they are, their positioning, size, transform, clipping, etc.. With this, you can create almost any kind of carousel experience you'd like.
Carousel
transitioning is a key part of their behavior. The control is therefore fully dynamic and interactive. You can move between frames in two ways. Either by skipping ahead or backward or by manually moving the Carousel to an x/y offset and then letting it transition to the best frame when manual movement is complete. `Carousel`s rely on a Transitioner
to manage the way they animate for all these movements. This allows a great deal of flexibility and customization.
`Presenter`s are responsible for updating frame state whenever the Carousel
adjusts anything. This allows for holistic animations. There are several built-in Presenters
that show how to create various effects as well.
val carousel = Carousel(
SimpleListModel(listOf(image1, image2, image3)),
itemVisualizer { item, previous, _ ->
when (previous) {
is DynamicImage -> previous.also { it.update(item) }
else -> DynamicImage(item)
}
}
).apply {
wrapAtEnds = true
acceptsThemes = false
behavior = object: CarouselBehavior<Deferred<Image>> {
override val presenter = LinearPresenter<Image>(spacing = 10.0) {
val aspectRatio = it.width.readOnly / it.height.readOnly
it.width eq parent.width
it.center eq parent.center
it.height eq it.width / aspectRatio
}
override val transitioner = dampedTransitioner<Image>(timer, animationScheduler) { _,_,_, update ->
animate(0f to 1f, using = tweenFloat(easeInOutCubic, duration = 1 * seconds)) {
update(it)
}
}
}
}
Animations Can Now Be Paused/Resumed
The `Animation`s implement the new Pausable
interface, which allows them to be paused and resumed at any time.
val animation = animate.invoke(0f to 1f, using = tweenFloat(easeInOutCubic, duration = 1 * seconds)) {
// ...
}
animation.pause()
// ..
animation.resume()
view
| container
Improvements
These DSLs now expose more of the protected properties of View
and Container
respectively.
view {
+ view {} // adds a child to the View
chidren += view {} // children now accessible
layout = simpleLayout {} // access layout
addedToDisplay = { } // called when view added to display
removedFromDisplay = { } // called when view removed from display
contains = { _ -> false } // called to decide if a point within the view
// ..
}
APIs
• Vector3D
now exposes its magnitude
• basicMenuBehavior
module function now exposes various configuration input parameters to customize the result.
• TreeItemRole
now has a selected
property
• New lerp
function for Rectangle
and Size
• New ``Rectangle`` constructor that takes a Size
• BasicDropdownBehavior
now takes accessibility label for its button
• BasicMutableDropdownBehavior
now takes accessibility label for its button
• BasicSpinnerBehavior
now takes accessibility labels for its buttons
• BasicMutableSpinnerBehavior
now takes accessibility labels for its buttons
• basicDropdownBehavior()
, basicMutableDropdownBehavior()
, basicSpinnerBehavior()
and basicMutableSpinnerBehavior()
now support accessibility labels
• MonthPanel
class is now open
Fixes | Improvements
• General
◦ Bug where MonthPanel
could NPE if selection queried while nothing selected
◦ Bug in TextMetrics
size calculation when lineSpacing
set
◦ TreeRow
now updates its accessibility role with the current selected state.
◦ Issue where deleted `View`s might not be cleaned up if they are removed from a parent at the same time their child is being removed and added to that same parent.
◦ Bug where View
could loop when a child is removed and added to its parent
◦ Camera
now clips transformed ConvexedPolygons
so their points do not go behind it.
◦ Issue where the children of a View
that is removed are not properly re-added if they are placed into their grandparent before the next cleanup pass within RenderManagerImpl
.
◦ Bug where View.toAbsolute
and View.fromAbsolute
resulted in incorrect results
◦ Optimize View.resolvedTransform
with caching
• Browser
◦ TreeRole
now marked as aria-multiselectable
◦ Native behavior for `HyperLink`s ensure the associated html element inherits the a11y labels of their Hyperlink
◦ Native behavior for `TextField`s ensure the associated html element inherits the a11y labels of their TextField
◦ Fixed issue where Native TextField
behavior stopped supporting mask value
◦ Fixed Issue with graphics surface sort order
◦ Line-height not correctly set when value != 1f
◦ Bug where zOrder
is incorrect for containers
◦ Only apply Safari shadow hack when multiple shadows applied to an element
◦ Case where graphics surface index could go negative and create incorrect render ordering
• Desktop
◦ Bug with zIndex
for pop-ups
Versions
• Measured -> 0.3.3Nick
08/30/2023, 5:30 AMNick
09/08/2023, 3:02 PMConstantin Birkert
10/30/2023, 4:32 PMAlessandro Marcolini
12/05/2023, 2:10 AMLuc Girardin
12/15/2023, 12:06 PMNick
12/16/2023, 3:00 AMLuc Girardin
12/16/2023, 11:25 AMNick
01/12/2024, 4:26 PMLuc Girardin
01/12/2024, 4:46 PMLuc Girardin
01/12/2024, 5:06 PMLuc Girardin
01/12/2024, 5:15 PMNick
01/17/2024, 8:49 PMwasmJS
build target. This means apps can also target WASM for the browser. The APIs/features for this new target are identical as those for the js
target; which means code can be shared between apps targeting both. The only difference is that the application
launchers need to be called from separate source sets (i.e. jsMain
vs wasmJsMain
).
Multi-window Support (Desktop)
Apps for Desktop can now create/manage multiple windows using a new WindowGroup
instance. This instance can be injected into an app just like the Display
. It then provides APIs for getting the main
window and creating new ones. Single window apps continue to work as they did before. That is, an app that injects the Display
will receive the main
window display and can manipulate it as before. But apps that want to manage their window(s) will need to inject this new type.
class MyCoolApp(windows: WindowGroup /*, mainWindowDisplay: Display*/): Application {
init {
// main window's display, same as if injected
windows.main.apply {
title = "Main Window"
// manipulate main window's display
display += view {}
}
// create a new window
windows().apply {
title = "A New Window!"
size = Size(500)
enabled = false
resizable = false
triesToAlwaysBeOnTop = true
// manipulate the new window's display
display += view {}
display.layout = constrain(display.first(), fill)
closed += {
// handle window close
}
}
}
override fun shutdown() {}
}
Native Window Menus (Desktop)
Apps can now set up native menus for their windows. This looks a lot like working with the existing menu APIs, but it results in changes to the OS window decoration. These menus are just as interactive as the in-app ones as well, meaning they trigger events when the user interacts with them.
window.menuBar {
menu("Menu 1") {
action("Do action 2", pathIcon) { /*..*/ }
menu("Sub menu") {
action("Do action sub", icon = simpleIcon) { /*..*/ }
separator()
prompt("Some Prompt sub") { /*..*/ }
}
separator()
prompt("Some Prompt") { /*..*/ }
}
menu("Menu 2") {
// ...
}
}
Native Window Context Menus (Desktop)
Apps can now set up native context/popup menus for their windows. The API is very similar to native menus.
window.popupMenu(at = somePoint) {
action("Do action 2", pathIcon) { /*..*/ }
menu("Sub menu") {
action("Do action sub", icon = simpleIcon) { /*..*/ }
separator()
prompt("Some Prompt sub") { /*..*/ }
}
separator()
prompt("Some Prompt") { /*..*/ }
}
APIs
• General
◦ PathBuilder
now supports moveTo
◦ Resizer
can now avoid updating a View's cursor if manageCursor = false
is provided to it at construct time.
◦ VerticalList
and HorizontalList
builders now take optional itemVisualizer.
◦ View.ClipPath.path
can now be overridden by subclasses.
◦ SetPool
no longer exposes a constructor, or underlying data structure, so it can be controlled.
◦ New PathModule
to allow use of PathMetrics
. PathMetricsImpl
is now internal.
◦ New MenuFactoryModule
to allow use of MenuFactory
. MenuFactoryImpl
is now internal.
◦ New UserPreferencesModule
to allow use of UserPreferences
. UserPreferencesImpl
is now internal.
◦ Deprecations
▪︎ Old <http://io.nacular.doodle.controls.menu|io.nacular.doodle.controls.menu>
package removed
▪︎ Label.horizontalAlignment
removed
▪︎ TextVisualizer
typealias removed
▪︎ Canvas.wrapped
methods removed
▪︎ inscribed
method removed from Polygon.kt
▪︎ PointerInputService.Listener.changed
method removed
▪︎ PointerInputService.Preprocessor.preprocess
method removed
• Browser
◦ Remove drag-drop support on IE
◦ Moved all DOM definitions to internal so they don't pollute the app space.
Fixes | Improvements
• General
◦ Issue where Carousel item not properly updated if skip
called when there is no transitioner
◦ Issue where work done during View.addedToDisplay
could cause unbroken loop.
◦ ModalManager
now clears focus when a new modal is shown and returns focus to the previous focus owner when that modal is completed.
◦ Issue in FocusManagerImpl
related to transitioning focus to a new View
◦ Issue in RenderManagerImpl
where new Views could be rendered during an ongoing render. This would result in concurrent modification errors since the latest Kotlin version adds guards for this in JS InternalMap.
• Browser
◦ Fixed pointer handling of ENTER
, which is called on scroll and new Views are added. This means pointer events are now delivered properly in these cases.
◦ Suppress scroll in native TextField behavior when element focused
◦ Issues with canvas clipping and transforms within custom FileSelector
behavior
◦ Handle selection attempt for input types that do not support it
◦ Fix issue where native FileSelector would not trigger filesChanged
if the same item was selected in subsequent viewings.
◦ Concurrent modification issue in SetPool
addressed w/ copy-on-write semantics
◦ No longer having DOM types resolved via instance()
in certain Kodein bindings since that results in WASM compiler crashes
◦ Avoiding concurrent modification issue with RenderManagerImpl.pendingBoundsChange
• Desktop
◦ Fixed crash on app shutdown
Build
• Fixed JS Source Maps
• Using TOML for buildSrc Kotlin version
Versions
• Kotlin -> 1.9.22
• Skiko -> 0.7.90
• DateTime -> 0.5.0
• Kodein -> 7.21.1
• Coroutines -> 1.8.0-RC
• Measured -> 0.4.0
• Kover -> 0.7.3
• Mockk -> 1.13.8
• Gradle -> 8.4bashor
01/18/2024, 4:18 PMAlessandro Marcolini
01/27/2024, 1:59 AMAlessandro Marcolini
01/27/2024, 2:01 AMNick
02/18/2024, 7:28 AMimport io.nacular.doodle.HtmlElementViewFactory
import io.nacular.doodle.application.Application
import io.nacular.doodle.core.Display
import org.w3c.dom.HTMLElement
class MyApp(
display : Display,
htmlElementView: HtmlElementViewFactory,
someElement : HTMLElement,
): Application {
init {
display += htmlElementView(element = someElement)
}
override fun shutdown() {}
}
application(modules = listOf(Modules.HtmlElementViewModule)) {
MyApp(display = instance(), viewFactory = instance(), element = element)
}
WASM JS Support
Doodle now supports the wasmJS
build target. This means apps can also target WASM for the browser. The APIs/features for this new target are identical as those for the js
target; which means code can be shared between apps targeting both. The only difference is that the application
launchers need to be called from separate source sets (i.e. jsMain
vs wasmJsMain
).
Multi-window Support (Desktop)
Apps for Desktop can now create/manage multiple windows using a new WindowGroup
instance. This instance can be injected into an app just like the Display
. It then provides APIs for getting the main
window and creating new ones. Single window apps continue to work as they did before. That is, an app that injects the Display
will receive the main
window display and can manipulate it as before. But apps that want to manage their window(s) will need to inject this new type.
class MyCoolApp(windows: WindowGroup /*, mainWindowDisplay: Display*/): Application {
init {
// main window's display, same as if injected
windows.main.apply {
title = "Main Window"
// manipulate main window's display
display += view {}
}
// create a new window
windows {
title = "A New Window!"
size = Size(500)
enabled = false
resizable = false
triesToAlwaysBeOnTop = true
// manipulate the new window's display
display += view {}
display.layout = constrain(display.first(), fill)
closed += {
// handle window close
}
}
}
override fun shutdown() {}
}
Native Window Menus (Desktop)
Apps can now set up native menus for their windows. This looks a lot like working with the existing menu APIs, but it results in changes to the OS window decoration. These menus are just as interactive as the in-app ones as well, meaning they trigger events when the user interacts with them.
window.menuBar {
menu("Menu 1") {
action("Do action 2", pathIcon) { /*..*/ }
menu("Sub menu") {
action("Do action sub", icon = simpleIcon) { /*..*/ }
separator()
prompt("Some Prompt sub") { /*..*/ }
}
separator()
prompt("Some Prompt") { /*..*/ }
}
menu("Menu 2") {
// ...
}
}
Native Window Context Menus (Desktop)
Apps can now set up native context/popup menus for their windows. The API is very similar to native menus.
window.popupMenu(at = somePoint) {
action("Do action 2", pathIcon) { /*..*/ }
menu("Sub menu") {
action("Do action sub", icon = simpleIcon) { /*..*/ }
separator()
prompt("Some Prompt sub") { /*..*/ }
}
separator()
prompt("Some Prompt") { /*..*/ }
}
Key events behave more like Pointer events
Key events now "sink" and "bubble" like pointer events. This means ancestor Views can intercept (and veto) them before they are delivered to their target (the focused View). They also bubble up to ancestors after being delivered to the target if they are not consumed. The notifications for the first phase happen via a new View.keyFilter
property, while the bubbling phase is notified via the existing View.keyChanged
property.Alessandro Marcolini
02/19/2024, 2:06 AMCannot inline bytecode built with JVM target 11 into bytecode that is being built with JVM target 1.8. Please specify proper '-jvm-target' option
. I've tried restarting the IDE, changing the target to 1.8, and removing all other dependencies.Nick
02/19/2024, 2:30 AMNick
02/19/2024, 3:29 AMAlessandro Marcolini
02/19/2024, 3:30 AMAlessandro Marcolini
02/19/2024, 3:31 AMAlessandro Marcolini
02/19/2024, 3:41 AMNick
02/19/2024, 3:43 AMAlessandro Marcolini
02/20/2024, 4:11 AMapplication
method launches the app in a different coroutine which the test does not wait for. As a workaround I have a companion object in my application class that stores when the app shuts down, and in the test I wait for that condition.Nick
02/20/2024, 4:39 AMAlessandro Marcolini
02/24/2024, 8:57 PMcommonMain/resources
are loaded correctly, but if I try to run it from a separate module, like in the Photos example, the images aren't loaded.Nick
02/24/2024, 9:00 PMConstantin Birkert
02/25/2024, 1:49 PM