blob: ddcca40484bfeab4d1b13bdf187e22fcd04b0f4f [file] [log] [blame]
// Copyright 2019 The Fuchsia 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:built_collection/built_collection.dart';
import 'package:flutter/material.dart';
import 'package:fuchsia_scenic_flutter/child_view.dart' show ChildView;
import 'package:fuchsia_scenic_flutter/child_view_connection.dart'
show ChildViewConnection;
import 'package:tiler/tiler.dart';
import '../tile_model/module_info.dart';
import 'widgets/drop_target_widget.dart';
import 'widgets/editing_tile_chrome.dart';
const _kSizerThickness = 24.0;
const _kSizerHandleThickness = 4.0;
final _kSizerHandleDecoration = BoxDecoration(
border: Border.all(color: Color(0xFFBDBDBD)),
borderRadius: BorderRadius.circular(2),
);
/// Tiling layout Layout presenter widget.
@immutable
class LayoutPresenter extends StatelessWidget {
/// The model being rendered.
final TilerModel tilerModel;
/// Whether edit mode is on or not.
final bool isEditing;
/// Maps a surface id to the view.
final BuiltMap<String, ChildViewConnection> connections;
/// Border color for the mod.
final Color Function(String modName) colorForMod;
/// Maps a parameter id to a color.
final Map<String, Color> parametersToColors;
/// Currently focused mod.
final ValueNotifier focusedMod;
/// Constructor for a tiling layout presenter.
const LayoutPresenter({
@required this.tilerModel,
@required this.isEditing,
@required this.connections,
@required this.colorForMod,
@required this.parametersToColors,
@required this.focusedMod,
});
@override
Widget build(BuildContext context) {
final tiler = Tiler(
model: tilerModel,
sizerThickness: isEditing ? _kSizerThickness : 0,
sizerBuilder: isEditing ? _sizerBuilder : null,
chromeBuilder: _buildChrome,
);
if (!isEditing) {
return tiler;
}
return AnimatedBuilder(
animation: tilerModel,
builder: (context, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
_addTarget(tileAfter: _getRoot, axis: Axis.horizontal),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
_addTarget(tileAfter: _getRoot, axis: Axis.vertical),
child,
_addTarget(tileBefore: _getRoot, axis: Axis.vertical),
],
),
),
_addTarget(tileBefore: _getRoot, axis: Axis.horizontal),
],
);
},
child: Expanded(child: tiler),
);
}
TileModel _getRoot() => tilerModel.root;
Widget _sizerBuilder(
BuildContext context,
Axis axis,
TileModel tileBefore,
TileModel tileAfter,
) =>
Stack(
children: <Widget>[
Container(
color: Colors.transparent,
// TODO(ahetzroni): wrap in AnimatedSize when variably sized sizers becomes available and adding drop targets
child: SizedBox(
width: axis == Axis.horizontal ? null : _kSizerThickness,
height: axis == Axis.vertical ? null : _kSizerThickness,
child: Center(
child: Container(
width: axis == Axis.horizontal
? _kSizerThickness
: _kSizerHandleThickness,
height: axis == Axis.vertical
? _kSizerThickness
: _kSizerHandleThickness,
decoration: _kSizerHandleDecoration,
),
),
),
),
Positioned.fill(
child: _addTarget(
tileBefore: () => tileBefore,
tileAfter: () => tileAfter,
axis: axis,
),
),
],
);
Widget _buildChrome(BuildContext context, TileModel tile) {
ModuleInfo content = tile.content;
final modName = content.modName;
final connection = connections[modName];
if (!isEditing) {
return ChildView(connection: connection);
}
return LayoutBuilder(
builder: (context, constraints) => EditingTileChrome(
focusedMod: focusedMod,
borderColor: colorForMod(modName),
parameterColors:
content.parameters.map((p) => parametersToColors[p]),
tilerModel: tilerModel,
tile: tile,
modName: modName,
childView: ChildView(
focusable: false,
hitTestable: false,
connection: connection,
),
editingSize: constraints.biggest,
originalSize: constraints.biggest +
Offset(_kSizerThickness, _kSizerThickness),
));
}
Widget _addTarget({
TileModel Function() tileBefore,
TileModel Function() tileAfter,
Axis axis,
}) =>
DropTargetWidget(
onAccept: (tile) {
tilerModel
..remove(tile)
..add(
content: tile.content,
nearTile: (tileBefore ?? tileAfter)(),
direction: tileBefore != null
? (axis == Axis.horizontal
? AxisDirection.down
: AxisDirection.right)
: (axis == Axis.horizontal
? AxisDirection.up
: AxisDirection.left),
);
},
onWillAccept: (tile) =>
tile != tileBefore?.call() && tile != tileAfter?.call(),
axis: axis == Axis.horizontal ? Axis.vertical : Axis.horizontal,
baseSize: _kSizerThickness,
hoverSize: _kSizerThickness,
);
}