Commit 6e96b146 authored by Hixie's avatar Hixie

Add Decoration.lerp and document Decoration

parent 9129891e
...@@ -8,8 +8,10 @@ ...@@ -8,8 +8,10 @@
/// engine's painting API for more specialised purposes, such as painting scaled /// engine's painting API for more specialised purposes, such as painting scaled
/// images, interpolating between shadows, painting borders around boxes, etc. /// images, interpolating between shadows, painting borders around boxes, etc.
/// ///
/// This library depends only on the core Dart libraries and animation.dart. /// In particular:
/// Note: animation.dart depends on the `newton` package. /// * Use the [TextPainter] class for painting text.
/// * Use [Decoration] (and more concretely [BoxDecoration]) for
/// painting boxes.
library painting; library painting;
export 'src/painting/basic_types.dart'; export 'src/painting/basic_types.dart';
......
...@@ -844,6 +844,8 @@ class BoxDecoration extends Decoration { ...@@ -844,6 +844,8 @@ class BoxDecoration extends Decoration {
/// Linearly interpolate between two box decorations. /// Linearly interpolate between two box decorations.
/// ///
/// Interpolates each parameter of the box decoration separately. /// Interpolates each parameter of the box decoration separately.
///
/// See also [Decoration.lerp].
static BoxDecoration lerp(BoxDecoration a, BoxDecoration b, double t) { static BoxDecoration lerp(BoxDecoration a, BoxDecoration b, double t) {
if (a == null && b == null) if (a == null && b == null)
return null; return null;
...@@ -933,6 +935,11 @@ class BoxDecoration extends Decoration { ...@@ -933,6 +935,11 @@ class BoxDecoration extends Decoration {
return result.join('\n'); return result.join('\n');
} }
/// Whether this [Decoration] subclass needs its painters to use
/// [addChangeListener] to listen for updates.
///
/// [BoxDecoration] objects only need a listener if they have a
/// background image.
@override @override
bool get needsListeners => backgroundImage != null; bool get needsListeners => backgroundImage != null;
...@@ -951,6 +958,8 @@ class BoxDecoration extends Decoration { ...@@ -951,6 +958,8 @@ class BoxDecoration extends Decoration {
// In principle, we should use shortestSide / 2.0, but we don't want to // In principle, we should use shortestSide / 2.0, but we don't want to
// run into floating point rounding errors. Instead, we just use // run into floating point rounding errors. Instead, we just use
// shortestSide and let Canvas do any remaining clamping. // shortestSide and let Canvas do any remaining clamping.
// The right long-term fix is to do layout using fixed precision
// arithmetic. (see also "_applyFloatingPointHack")
return borderRadius > shortestSide ? shortestSide : borderRadius; return borderRadius > shortestSide ? shortestSide : borderRadius;
} }
......
...@@ -9,22 +9,105 @@ export 'edge_insets.dart' show EdgeInsets; ...@@ -9,22 +9,105 @@ export 'edge_insets.dart' show EdgeInsets;
// This group of classes is intended for painting in cartesian coordinates. // This group of classes is intended for painting in cartesian coordinates.
/// A description of a box decoration (a decoration applied to a [Rect]).
///
/// This class presents the abstract interface for all decorations.
/// See [BoxDecoration] for a concrete example.
///
/// To actually paint a [Decoration], use the [createBoxPainter]
/// method to obtain a [BoxPainter]. [Decoration] objects can be
/// shared between boxes; [BoxPainter] objects can cache resources to
/// make painting on a particular surface faster.
abstract class Decoration { abstract class Decoration {
/// Abstract const constructor.
const Decoration(); const Decoration();
/// In checked mode, throws an exception if the object is not in a
/// valid configuration. Otherwise, returns true.
///
/// This is intended to be used as follows:
/// ```dart
/// assert(myDecoration.debugAssertValid());
/// ```
bool debugAssertValid() => true; bool debugAssertValid() => true;
/// Returns the insets to apply when using this decoration on a box
/// that has contents, so that the contents do not overlap the edges
/// of the decoration. For example, if the decoration draws a frame
/// around its edge, the padding would return the distance by which
/// to inset the children so as to not overlap the frame.
EdgeInsets get padding => null; EdgeInsets get padding => null;
/// Linearly interpolates from [a] to [this].
Decoration lerpFrom(Decoration a, double t) => this; Decoration lerpFrom(Decoration a, double t) => this;
/// Linearly interpolates from [this] to [b].
Decoration lerpTo(Decoration b, double t) => b; Decoration lerpTo(Decoration b, double t) => b;
/// Linearly interpolates from [begin] to [end].
///
/// This defers to [end]'s [lerpTo] function if [end] is not null,
/// otherwise it uses [begin]'s [lerpFrom] function.
static Decoration lerp(Decoration begin, Decoration end, double t) {
if (end != null)
return end.lerpTo(begin, t);
if (begin != null)
return begin.lerpFrom(end, t);
return null;
}
/// Tests whether the given point, on a rectangle of a given size,
/// would be considered to hit the decoration or not. For example,
/// if the decoration only draws a circle, this function might
/// return true if the point was inside the circle and false
/// otherwise.
bool hitTest(Size size, Point position) => true; bool hitTest(Size size, Point position) => true;
/// Whether this [Decoration] subclass needs its painters to use
/// [addChangeListener] to listen for updates. For example, if a
/// decoration draws a background image, owners would have to listen
/// for the image's load completing so that they could repaint
/// themselves when appropriate.
bool get needsListeners => false; bool get needsListeners => false;
/// Register a listener. See [needsListeners].
///
/// Only call this if [needsListeners] is true.
void addChangeListener(VoidCallback listener) { assert(false); } void addChangeListener(VoidCallback listener) { assert(false); }
/// Unregisters a listener previous registered with
/// [addChangeListener]. See [needsListeners].
///
/// Only call this if [needsListeners] is true.
void removeChangeListener(VoidCallback listener) { assert(false); } void removeChangeListener(VoidCallback listener) { assert(false); }
/// Returns a [BoxPainter] that will paint this decoration.
BoxPainter createBoxPainter(); BoxPainter createBoxPainter();
@override @override
String toString([String prefix = '']) => '$prefix$runtimeType'; String toString([String prefix = '']) => '$prefix$runtimeType';
} }
/// A stateful class that can paint a particular [Decoration].
///
/// [BoxPainter] objects can cache resources so that they can be used
/// multiple times.
abstract class BoxPainter { // ignore: one_member_abstracts abstract class BoxPainter { // ignore: one_member_abstracts
/// Paints the [Decoration] for which this object was created on the
/// given canvas using the given rectangle.
///
/// If this object caches resources for painting (e.g. [Paint]
/// objects), the cache may be flushed when [paint] is called with a
/// new [Rect]. For this reason, it may be more efficient to call
/// [Decoration.createBoxPainter] for each different rectangle that
/// is being painted in a particular frame.
///
/// For example, if a decoration's owner wants to paint a particular
/// decoration once for its whole size, and once just in the bottom
/// right, it might get two [BoxPainter] instances, one for each.
/// However, when its size changes, it could continue using those
/// same instances, since the previous resources would no longer be
/// relevant and thus losing them would not be an issue.
void paint(Canvas canvas, Rect rect); void paint(Canvas canvas, Rect rect);
} }
...@@ -20,13 +20,7 @@ class DecorationTween extends Tween<Decoration> { ...@@ -20,13 +20,7 @@ class DecorationTween extends Tween<Decoration> {
DecorationTween({ Decoration begin, Decoration end }) : super(begin: begin, end: end); DecorationTween({ Decoration begin, Decoration end }) : super(begin: begin, end: end);
@override @override
Decoration lerp(double t) { Decoration lerp(double t) => Decoration.lerp(begin, end, t);
if (begin == null && end == null)
return null;
if (end == null)
return begin.lerpTo(end, t);
return end.lerpFrom(begin, t);
}
} }
/// An interpolation between two [EdgeInsets]s. /// An interpolation between two [EdgeInsets]s.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment