github nacular/doodle v0.11.0
0.11.0

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

Features

New Layout Paradigm

A View's bounds is no longer editable directly as it was in previous versions of Doodle. This is a major change to the way Doodle layout functions; but it is important to avoid some major pitfalls of the previous approach. Namely, it was very easy to write code that would not produce the expected layout. This is a good example of something that would cause issues before:

Doodle 0.10.x

view {
    + Label("Hello") // Label fits its text by default
  
    layout = constrain(children.first(), fill) // ❌ Label would ignore layout
}

Doodle 0.11.0

view {
    + Label("Hello") // Label fits its text by default
  
    layout = constrain(children.first(), fill) // ✅ works as expected
}

Now it just works as expected since View's cannot override the Layout they are managed by.

More specifically, a View is given a min and max size it can take by its parent's Layout. It then picks a size it would like to take in that range and reports back to the Layout, which uses that information to position/size it and the other Views it manages.

This means you cannot directly change a View's bounds like before. However, you can still suggest changes that the View may use to determine its final size.

This means code like this no longer works, and cannot be directly converted to the new suggestion system. That's because bounds suggestions are not guaranteed in the way a normal setter would be.

Doodle 0.10.x

val view1 = view {}.apply { size = Size(10, 50) }
val view2 = view {}.apply { size = view1.size   } // view2.size == Size(10, 50)

Doodle 0.11.0

val view1 = view {}.apply { suggestSize(10, 50) }
val view2 = view {}.apply { size = view1.size   } // view2.size == Size.Empty since suggestion usually async

Frosted Glass Paint

New Paint that lets you create glass like material that blurs the underlying content. This, like all paints, can be used to fill any shape, stroke, or text.

rect(
    rectangle = bounds.atOrigin.inset(borderThickness / 2),
    radius    = cardRadius,
    fill      = FrostedGlassPaint(Color(0x09008bu) opacity 0.2f, blurRadius = 10.0)
)

Text Outlining

You can now outline text using Strokes like other shapes. This includes StyledText, which now supports strokes for styled segments.

render = {
    text(
        text   = "Hello Doodle!",
        at     = Origin,
        fill   = Transparent.paint,
        stroke = Stroke()
    )
}
val stroke = Stroke()

render = {
    text(
        text = "Hello " .. stroke { "Doodle!" },
        at   = Origin,
    )
}

Inline Constraints for Forms

Forms have <api.Layout/>s that you can specify explicitly. But now you can also define constraint-based layouts declaratively when defining a Form.

Form { this (
    "Bob" to labeled("Name" ) { textField (                                  ) },
    21    to labeled("Age"  ) { spinButton(1..120                            ) },
    Green to labeled("Color") { colorStrip(Red, Green, Blue, BlueColor, Black) },

    layout = { name, age, color ->
        name.top      eq parent.insets.top
        name.left     eq parent.insets.left
        name.right    eq age.left - 12 strength Strong
        name.height   eq name.idealHeight

        age.top       eq name.top
        age.width     eq 80
        age.right     eq parent.right - parent.insets.right
        age.height    eq age.idealHeight

        color.left    eq name.top
        color.top     eq name.bottom + 12
        color.right   eq age.right strength Strong
        color.height  eq color.preferredSize(
            min = Empty,
            max = Size(parent.width.readOnly, POSITIVE_INFINITY)
        ).height

        parent.bottom eq color.bottom + parent.insets.bottom
    },

    onInvalid = {}

) { name: String, color: Int, age: Color ->
    // ...
} }

APIs

  • General
    • ScrollPanelVisualizer now takes a config that lets you modify the resulting panel
    • ScrollPanel's contentWidthConstraints and contentHeightConstraints now provide IdealSizedProperty values, which have an idealValue field. This lets you constrain the property using its ideal value.
    • Layout now has preferredSize method, which returns the preferred size for the set of Views given the current context.
    • lerp for HsvColor
    • parent insets now available within constraint blocks
    • underline and lineThrough helpers for creating TextDecorations
    • New Encoder utility types for Strings
    • New methods for scrolling a View horizontally and vertically
    • New infix strength method for constraint DSL (removed rangeTo operator option)
    • Removed Label.fitText
    • Paths can now be created by "extending" an existing one. This produces a builder based on the given path
    • PathBuilder has a new method to append a Path
    • CarouselItem now has a displayIndex property which indicates the item's display order relative to nearestItem.
    • New MonthPanel.showMaxRows property that controls whether all 6 potential rows are shown for the panel.
    • TreeBehavior.RowPositioner now gets tree bounds info as an input for rowBounds and contentBounds
    • New Theme.selected and Theme.deselected events to indicate when a Theme has been selected/deselected.
    • MenuBehavior.SubMenuConfig now allows configuration of the menu's anchor point, insets from the Display's edges, and horizontal/vertical offsets from the parent menu.
    • TileLayout now takes an Orientation
    • New anchor property for SlicerPresenter that controls which part of the stack moves first.
    • ScrollPanelVisualizer now takes a config that lets you modify the resulting panel
    • ScrollPanel's contentWidthConstraints and contentHeightConstraints now provide IdealSizedProperty values, which have an idealValue field. This lets you constrain the property using its ideal value.
    • ValueSlider.snapSize is no longer public
    • New Image.aspectRatio property

Fixes | Improvements

  • General

    • bug in interiorAngle function for 2 vectors
    • spinButton form field now adopts initial value set for it.
    • IntSpinButtonModel now clamps initial value
    • Issue in Resizer where pointer release was consumed (to avoid issues on touch devices) which caused strange behavior w/ other handlers. Changed to preventOsHandling instead.
    • Bugs in ConstrainedSizePolicy
    • Form switch layout
    • Label only re-measures text if text is actually changed
    • Some areas where wordSpacing and letterSpacing couldn't be 0
    • GridLayout issue due to new layout model
    • Issue where ConstraintLayoutImpl would unregister context for Views that were unconstrained even if they were still constrained in other blocks.
    • Issue where ScrollPanel scroll didn't take scroll bar sizes into account
    • Selection bug in Lists, Tables, Tress, TreeTables related to selecting the previous item after a select all
    • Bug where MultiSelectionModel wouldn't have correct last when selecting all while last item
    • Fixed sizing for many form controls that were incorrect under new layout scheme
    • Issue where List could reset size incorrectly if it's parent has no layout
    • Label default lineSpacing
    • Label preferredSize now tracks bounds changes when wrapsWords set to true
    • The way offset/size-inset constraints work
    • BasicSelectBoxBehavior rendering
    • BasicSpinButtonBehavior rendering
    • Issue where Label.text wouldn't update if changing from a StyledText with same text value
    • Edge case where Carousel wouldn't transition if it only contained 1 item with wrapping
    • Menu selection issue caused by bad Kotlin compilation: https://youtrack.jetbrains.com/issue/KT-73130.
    • Edge case where items could be selected in a Menu if they were all disabled.
    • Issue where Carousel would incorrectly handle manual move cancellation if no movement had occurred
    • Bug in they way Views are selected, which caused disabled Views to get pointer events.
    • Render issues in CubePresenter
    • Issue where Carousel would incorrectly handle manual move cancellation if no movement had occurred
    • Issue where Carousel wouldn't cancel skips properly and this resulted in janky rendering.
    • SlicerPresenter now caches bounds data to improve frame rate.
    • Issue with CubePresenter positioning of cube cap supplemental view.
    • BasicSelectBoxBehavior popup list size
    • Updated Dynamic Behaviors so they work w/ List<BehaviorResolver> instead of Set to ensure proper ordering
    • bug in ProportionalSizePolicy that led to incorrect column sizing
    • Popup ordering in some edge cases
    • Stroke rendering with some paints
    • But where PatternPaint transform not updated in some edge cases
    • Render issue with some brushes when the content they render is clipped
    • Issue in native ScrollPanel behavior that prevented proper install/uninstall cycles
    • No longer returning 0 for empty string height
    • Issue with paint caching
    • Using browser's default line-height instead of assuming a value of 1.
    • Incorrectly treating "simple" brushes as complex in some edge cases, which means they'd render using SVG when they didn't need to
    • Ensure single line text has proper line height set
    • Various fixes for foreign object paints
    • Issue where some paints could fail to draw both strokes and fill when used at the same time
    • Issue where some paints would prevent fill from working when used as a Stroke
    • Issue in stroke rendering with SweepGradientPaint
    • Incorrect text indent for wrapped styled text in some cases.
    • Vertical alignment of SVG text so it matches HTML text.
    • SVG text backgrounds
    • Bug where touch events resulted in pointer enter being called after exit. This meant Views that were touched would always remain in the "pointer entered" state, which breaks many app interactions.
    • Layout bug where new bounds could be lost for View within a constraint layout
    • Bug where focus could get stuck in a loop if all top-level views became non-focusable
    • Constraint layout issue where position might not be updated
    • Issue where View was not relying on layout for child hit detection in all cases
  • Browser

    • Text color not defaulting to black in some cases
    • Pointer up event consumed even if down event was not sent to the app
    • Bug in logic that decides when to use plain css for text
    • Bug in text width calculation for StyledText
    • Render bug where StyledText background would cover an entire line of text instead of the specific words.
    • Work-around for Safari's CanvasRenderingContext2D not having proper Font support
  • Desktop

    • Reduced render lag by disabling skiko vsync
    • Default line height handling
    • Default line height handling
    • Opacity not used for image and image paints
    • Updating where skiko properties are set to ensure they take effect, this includes better integration of windows w/ Mac system appearance.
    • Issue with outer shadows not rendering properly
    • Issue where text could wrap incorrectly at small widths
    • Issue when rendering indent for wrapped, plain text.
    • Issue where top-level view might not be properly erased when removed
    • Hit detection for ScrollPanels using native behavior
    • Render artifact when resizing ScrollPanels using native behavior
    • Using skia's transparent constant directly

Build

  • Type-safe project accessors
  • Cleanup deprecations in buildSrc
  • Remove node version specification
  • Removed deprecated use of compilerOptions
  • Set buildSrc project name to remove build warning

Versions

  • Kotlin -> 2.1.10
  • Kover -> 0.9.1
  • Mockk -> 1.13.13
  • Skiko -> 0.9.2

Don't miss a new doodle release

NewReleases is sending notifications on new releases.