• Ian Hickson's avatar
    Move image logic from services/ to painting/. (#13409) · 44e228eb
    Ian Hickson authored
    This allows the scheduler library to depend on the services library
    and the painting library to depend on the scheduler library without
    the services library having to depend on the scheduler library.
    
    While I was at it I also cleaned up some of the binding logic: the
    licenses logic can now be overridden (and the test library does so),
    and the image cache can now be overridden as well.
    Unverified
    44e228eb
rendering_tester.dart 3.95 KB
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';

import 'package:flutter_test/flutter_test.dart' show EnginePhase;
export 'package:flutter_test/flutter_test.dart' show EnginePhase;

class TestRenderingFlutterBinding extends BindingBase with ServicesBinding, GestureBinding, SchedulerBinding, PaintingBinding, RendererBinding {
  EnginePhase phase = EnginePhase.composite;

  @override
  void drawFrame() {
    assert(phase != EnginePhase.build, 'rendering_tester does not support testing the build phase; use flutter_test instead');
    pipelineOwner.flushLayout();
    if (phase == EnginePhase.layout)
      return;
    pipelineOwner.flushCompositingBits();
    if (phase == EnginePhase.compositingBits)
      return;
    pipelineOwner.flushPaint();
    if (phase == EnginePhase.paint)
      return;
    renderView.compositeFrame();
    if (phase == EnginePhase.composite)
      return;
    pipelineOwner.flushSemantics();
    if (phase == EnginePhase.flushSemantics)
      return;
    assert(phase == EnginePhase.flushSemantics ||
           phase == EnginePhase.sendSemanticsUpdate);
  }
}

TestRenderingFlutterBinding _renderer;
TestRenderingFlutterBinding get renderer {
  _renderer ??= new TestRenderingFlutterBinding();
  return _renderer;
}

/// Place the box in the render tree, at the given size and with the given
/// alignment on the screen.
///
/// If you've updated `box` and want to lay it out again, use [pumpFrame].
///
/// Once a particular [RenderBox] has been passed to [layout], it cannot easily
/// be put in a different place in the tree or passed to [layout] again, because
/// [layout] places the given object into another [RenderBox] which you would
/// need to unparent it from (but that box isn't itself made available).
///
/// The EnginePhase must not be [EnginePhase.build], since the rendering layer
/// has no build phase.
void layout(RenderBox box, {
  BoxConstraints constraints,
  Alignment alignment: Alignment.center,
  EnginePhase phase: EnginePhase.layout,
}) {
  assert(box != null); // If you want to just repump the last box, call pumpFrame().
  assert(box.parent == null); // We stick the box in another, so you can't reuse it easily, sorry.

  renderer.renderView.child = null;
  if (constraints != null) {
    box = new RenderPositionedBox(
      alignment: alignment,
      child: new RenderConstrainedBox(
        additionalConstraints: constraints,
        child: box
      )
    );
  }
  renderer.renderView.child = box;

  pumpFrame(phase: phase);
}

void pumpFrame({ EnginePhase phase: EnginePhase.layout }) {
  assert(renderer != null);
  assert(renderer.renderView != null);
  assert(renderer.renderView.child != null); // call layout() first!
  renderer.phase = phase;
  renderer.drawFrame();
}

class TestCallbackPainter extends CustomPainter {
  const TestCallbackPainter({ this.onPaint });

  final VoidCallback onPaint;

  @override
  void paint(Canvas canvas, Size size) {
    onPaint();
  }

  @override
  bool shouldRepaint(TestCallbackPainter oldPainter) => true;
}


class RenderSizedBox extends RenderBox {
  RenderSizedBox(this._size);

  final Size _size;

  @override
  double computeMinIntrinsicWidth(double height) {
    return _size.width;
  }

  @override
  double computeMaxIntrinsicWidth(double height) {
    return _size.width;
  }

  @override
  double computeMinIntrinsicHeight(double width) {
    return _size.height;
  }

  @override
  double computeMaxIntrinsicHeight(double width) {
    return _size.height;
  }

  @override
  bool get sizedByParent => true;

  @override
  void performResize() {
    size = constraints.constrain(_size);
  }

  @override
  void performLayout() { }

  @override
  bool hitTestSelf(Offset position) => true;
}