github nacular/doodle v0.9.1
0.9.1

latest releases: v0.11.5, v0.11.4, v0.11.3...
2 years ago

Dokka APIs Available

API docs for Doodle are now available at https://nacular.github.io/doodle-api/

Features

New PopupManager

You can now present top-level Views that will cover all other existing ones in a safe and reliable way using the new PopupManager. Before, you could make custom popups by directly adding items to the Display. This worked in many cases, but had a lot of limitations. For example, Views added this way would be affected by any Layout on the Display, which meant you couldn't control these Popups as easily. Also, new Views added to the Display after a custom popup was shown could overlay the popup. So there was no way to guarantee that a popup remained the top-most View. The new PopupManager fixes these limitations and provides a simple API for showing popups.

popupManager.show(view) {
    // size / position popup
    it.height eq parent.height / 2
    it.width  eq it.height * 1.5
    it.center eq parent.center
}

Popups can be positioned relatvie to another View as well by doing the following.

popups.show(view, relativeTo = someView) { popup, someViewRect ->
      // size / position popup
      (popup.top    eq       someViewRect.y         ) .. Strong
      (popup.left   eq       someViewRect.right + 10) .. Strong
      (popup.bottom lessEq   parent.bottom      -  5) .. Strong

      popup.top    greaterEq 5
      popup.left   greaterEq 5
      popup.right  lessEq    parent.right - 5
      popup.height eq        parent.height / 2
      popup.width  eq        popup.height * 1.5
  }

Any View Can Be A Popup

The PopupManager's API works with View, so you can make anything a popup. Simply do the following:

popupManager.show(myView) {
    it.center eq parent.center
}

// ...

popupManager.hide(myView)

Views that are shown as popups will automatically become top-level (being removed from their existing parent if already displayed) and sit above all other existing Views. Hiding a popup removes it from the Display, but it won't return the View to a previous parent if it had one.

Popup Layouts

Popups are shown with layout information directly. There are two ways to show a popup: relative to the Display, or relative to another View and the Display. There is an API call for each:

Here, myView is positioned with only a "reference" to the Display (parent).

popupManager.show(myView) {
    it.center eq parent.center
}

But sometimes a popup needs to be positioned relative to another View. This shows how to place myView so it tracks the bounds of someView. The PopupManager will handle keeping the popup aligned with someView.

// myView is positioned with only a "reference" to the Display (parent)
popupManager.show(myView, relativeTo = someView) { popup, anchor ->
    popup.top     greaterEq 0                             // Popup top-left always visible
    popup.left    greaterEq 0                             // Popup top-left always visible
    popup.width.preserve                                  // don't shrink popup
    popup.height.preserve                                 // don't shrink popup

   (popup.right   lessEq    parent.right ) .. Strong      // stay in parent as long as doesn't compress popup
   (popup.bottom  lessEq    parent.bottom) .. Strong      // stay in parent as long as doesn't compress popup 
  
   (popup.top     eq        anchor.bottom + 10) .. Medium // follow anchor, as long as stronger constraints not in conflict 
   (popup.centerY eq        anchor.centerY    ) .. Medium // follow anchor, as long as stronger constraints not in conflict
}

New ModalManager

Modals are now easy to incorporate into your apps with the help of the new ModalManager. This component uses the PopupManager to display Views that behave like modals (they require user input to be dismissed). Modals are strongly typed and return a single value upon completion. They also have a customizable overlay that will obscure the underlying Views if painted.

// launch modal and await result (suspending)
val value: T = modalManager {
    Modal(
        // View used as modal
        view {
            // ...

            // call completed when the modal is done
            completed(result)
        }
    ) {
        // optionally provide a layout block
        // or the view will default to being
        // displayed in the center
    }
}

Modals can also be positioned relative to another View.

// launch modal and await result (suspending)
val value: T = modalManager {
    RelativeModal(
        // View used as modal
        view {
            // ...

            // call completed when the modal is done
            completed(result)
        },
        relativeTo = someView
    ) { modal, someViewBounds ->

        // position relative to parent and someView
    }
}

More Powerful Text Rendering

Text Alignment

There is a new TextAlignment enum that controls how wrapped text is displayed within its margins. This is a replacement for HorizontalAlignment, which currently uses Left, Right (instead of Start, End) and does not support Justify. You can justify text by doing the following:

canvas.wrapped(
  text,
  at        = Origin,
  width     = width,
  alignment = TextAlignment.Justify,
  fill      = Black.paint
)

canvas.wrapped(
  styledText,
  at        = Origin,
  width     = width,
  alignment = TextAlignment.Justify,
)

Letter, Word, And Line Spacing

You can now control the way letters, words and lines are spaced when rendering text (via Canvas.text(...) and Canvas.wrapped(...)). Letter and words spacing can be provided to the text rendering methods on Canvas using the new TextSpacing class. This information can also be passed to TextMetrics when measuring text. Label also has support for both letter and word spacing.

Line spacing can also be specified whenever you deal with wrapped text.

New LazyPhoto Widget

This new component (in controls lib) takes a Deferred<Image> and renders it when loading is complete. It also takes a custom renderer that it uses to draw itself while loading. This allows customization and even animation.

APIs

  • Rectangle.toPath that allows specifying each corner radius
  • Exposing insets in simpleTextButtonRenderer function
  • repeat and loop animations can now have delays
  • Canvas can now render wrapped text with custom line-spacing
  • Label now has lineSpacing property which controls how it displays wrapped text
  • Animatable Properties are no longer restricted to use within Views
  • New method to create Ellipse and Circle by inscribing within Rectangle
  • New Circle.diameter property
  • New constructor for StyledText that takes a String and Style
  • Updated parameters in Canvas.wrapped methods to make clearer (i.e. indent/width vs leftMargin/rightMargin)
  • Added new StyledTextVisualizer to map StyledText -> Label
  • Deprecated TextVisualizer and introduced StringVisualizer
  • defaultLayout in NamedConfig (labeled form control) now takes an optional itemHeight
  • New tableCellEditor DSL

Accessibility

  • Browser
    • Button now sets accessibilityLabel to its text if no label is already provided
    • HyperLink with native behavior will now apply aria-label to the anchor tag using the HyperLink's text when the AccessibilityManager is present

Fixes | Improvements

  • General
    • Bug with empty AnimationBlock when created during active animation
    • Wrapped text not correctly aligned
    • Bug in FilteredList iterator.remove
    • CommonLabelBehavior no longer provides an x offset for wrapped text since the alignment is handled correctly by Canvas
    • Edge case where layout can hang b/c of double imprecision
    • Issue where CommonLabelBehavior overrides Label.foregroundColor on uninstall
    • Bug where incorrect item could be removed from RenderManagerImpl.pendingLayout
    • Bug where some View properties lost during behavior install
    • Bug where some views not cleaned up by RenderManager
    • Bug in pointer handling when Display transformed
    • ToggleButton no longer relies on being displayed to listen to its model
    • StyledText.text now returns correct value
    • behavior delegate now re-renders the View when a new behavior is installed. This removes the need for Behaviors to call render explicitly upon install
    • Bug where DynamicList would have incorrect selection when selection set before items added
  • Browser
    • Misidentifying elements as native scroll panels led to incorrect pointer behavior
    • Reuse instances for linear/radial gradient paints
    • Reusing clipped images
    • Hyperlinks now open in new tab
    • Shadow render bug when using SVG
    • Element sometimes not reused when drawing shadow
    • Remove overflow on element w/ shadow
    • Fixed wrapped StyledText rendering
    • Wrapped StyledText now supports text decoration on previously unsupported cases
    • Wrapped text no longer indents if the indent would result in the first word overflowing the width
    • Issue where reused text element could retain wrapped styles
    • Issue where text background color isn't properly cleared
    • Cleaning up text-alignment for reused <b> elements
    • Issue where styled text width not properly cleared
  • Desktop
    • Fixed SVG image file loading issue
    • Fixed rendering of images with radius
    • Issue where font families weren't being properly tracked and therefore incorrectly applied
    • Incorrect paragraph width measurement

Versions

  • Dokka -> 1.8.10
  • Skiko -> 0.7.44

Don't miss a new doodle release

NewReleases is sending notifications on new releases.