github nacular/doodle v0.10.0
0.10.0

latest releases: v0.11.5, v0.11.4, v0.11.3...
22 months ago

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.shouldHandlePointerEvent and View.shouldHandlePointerMotionEvent.
    • It is also possible to be notified of pointer events that are passed through a View using the new View.pointerPassedThrough and View.pointerMotionPassedThrough properties.
    • 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 allowPointerThrough property. Setting it to true will allow all pointer events to pass through the background; while still notifying pointerOutsideModalChanged and pointerMotionOutsideModalChanged.
    • New View.toParent method for taking a point from a View's coordinate space to its parent's
    • 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.
    • New Rectangle.centered(at) utility method.
    • New PatternPaint.opacity property.
    • Paint constructor now internal.
    • Positions in constraints can now be added/subtracted with Point.
    • Resizer can now have movable property set via constructor.
    • New Ellipse.inset method for insetting an ellipse
    • Moved Circle.inscribed up to Ellipse so polygons can be embedded within ellipses as well.
    • NumericAnimationPlan.invoke parameter renamed to onChange for consistency/clarity.
    • Make Modules constructors private.
    • Deprecations
      • Old 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.
    • Maked NativeFocusManager @internal and sealed
  • Desktop

    • createApplication should've been @Internal.

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.
    • Edge case where View's transform could be out-of-sync for right-left Views that need mirroring.
    • Improved Resizer drag for some cases when the target View has a transform.
    • Issue where zero sized View might get stuck in render pipeline.
    • Issue with incorrect time provided by AnimatedScheduler.onNextFrame.
    • Support for ImagePaint
  • 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
    • 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.
  • 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

Don't miss a new doodle release

NewReleases is sending notifications on new releases.