Features
Hosting arbitrary HTML elements
You can now embed any HTML element into your app as a View. This means Doodle apps can now host React and other web components and interoperate with a much larger part of the Web ecosystem out of the box!
import 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 WebAssymbly 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.
APIs
-
General
- View's can now pass pointer events through to underlying views. This is managed by the value returned by
View.shouldHandlePointerEventandView.shouldHandlePointerMotionEvent. - It is also possible to be notified of pointer events that are passed through a View using the new
View.pointerPassedThroughandView.pointerMotionPassedThroughproperties. - Modals can now specify how their backgrounds affect other modals that they overlay. This is configured via
ModalContext.backgroundMode. - Modals can now have pointer events pass through using the new
allowPointerThroughproperty. Setting it to true will allow all pointer events to pass through the background; while still notifyingpointerOutsideModalChangedandpointerMotionOutsideModalChanged. - New
View.toParentmethod for taking a point from a View's coordinate space to its parent's PathBuildernow supportsmoveToResizercan now avoid updating a View's cursor ifmanageCursor = falseis provided to it at construct time.VerticalListandHorizontalListbuilders now take optionalitemVisualizerView.ClipPath.pathcan now be overridden by subclasses.SetPoolno longer exposes a constructor, or underlying data structure, so it can be controlled.- New
PathModuleto allow use ofPathMetrics.PathMetricsImplis now internal. - New
MenuFactoryModuleto allow use ofMenuFactory.MenuFactoryImplis now internal. - New
UserPreferencesModuleto allow use ofUserPreferences.UserPreferencesImplis now internal. - New
Rectangle.centered(at)utility method. - New
PatternPaint.opacityproperty. Paintconstructor now internal.- Positions in constraints can now be added/subtracted with
Point. Resizercan now havemovableproperty set via constructor.- New
Ellipse.insetmethod for insetting an ellipse - Moved
Circle.inscribedup toEllipseso polygons can be embedded within ellipses as well. NumericAnimationPlan.invokeparameter renamed toonChangefor consistency/clarity.- Make
Modulesconstructors private. - Deprecations
- Old io.nacular.doodle.controls.menu package removed
Label.horizontalAlignmentremovedTextVisualizertypealiasremovedCanvas.wrappedmethods removedinscribedmethod removed fromPolygon.ktPointerInputService.Listener.changedmethod removedPointerInputService.Preprocessor.preprocessmethod removed
- View's can now pass pointer events through to underlying views. This is managed by the value returned by
-
Browser
- Remove drag-drop support on IE
- Moved all DOM definitions to internal, so they don't pollute the app space.
- Maked
NativeFocusManager@internal andsealed
-
Desktop
createApplicationshould've been@Internal.
Fixes | Improvements
-
General
- Issue where Carousel item not properly updated if
skipcalled when there is notransitioner - Issue where work done during
View.addedToDisplaycould cause unbroken loop. ModalManagernow clears focus when a new modal is shown and returns focus to the previous focus owner when that modal is completed.- Issue in
FocusManagerImplrelated to transitioning focus to a new View - Issue in
RenderManagerImplwhere 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. - Edge case where
View's transform could be out-of-sync for right-left Views that need mirroring. - Improved
Resizerdrag for some cases when the targetViewhas a transform. - Issue where zero sized View might get stuck in render pipeline.
- Issue with incorrect time provided by
AnimatedScheduler.onNextFrame. - Support for
ImagePaint
- Issue where Carousel item not properly updated if
-
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
TextFieldbehavior when element focused - Issues with canvas clipping and transforms within custom
FileSelectorbehavior - Handle selection attempt for input types that do not support it
- Fix issue where native
FileSelectorwould not triggerfilesChangedif the same item was selected in subsequent viewings. - Concurrent modification issue in
SetPooladdressed 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 - Fixed issue where Chrome would raise invalid key events (null properties) when doing autofill for form fields.
- Fixed Pointer handling for apps nested Views from
ApplicationViewFactory. - Issue where browser could get into loop with Mutation Observer events.
- Issue with shadow clipping for SVG rendering.
- Arc and Wedge not properly outlining.
- Fixed pointer handling of
-
Desktop
- Fixed crash on app shutdown
Build
- Fixed JS Source Maps
- Using toml for buildSrc kotlin version
- Add expect-actual-classes
- Minor improvements to buildSrc code
- Adding WasmJs to signing dependency chain
Versions
- Kotlin -> 1.9.22
- Skiko -> 0.7.90
- DateTime -> 0.5.0
- Kodein -> 7.21.1
- Coroutines -> 1.8.0
- Measured -> 0.4.0
- Kover -> 0.7.3
- Mockk -> 1.13.8
- Gradle -> 8.4