| // 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 'dart:collection'; |
| |
| import 'package:built_collection/built_collection.dart'; |
| import 'package:fuchsia_scenic_flutter/child_view_connection.dart'; |
| import 'package:tiler/tiler.dart'; |
| |
| import '../layout.dart'; |
| import '../tile_model/module_info.dart'; |
| import '../tile_model/tile_layout_model.dart'; |
| import '../tile_presenter/tile_presenter.dart'; |
| import 'layout_policy.dart'; |
| import 'layout_store.dart'; |
| import 'layout_utils.dart'; |
| |
| // The user has requested a different layout. |
| typedef UserLayoutRequestCallback = void Function(TilerModel<ModuleInfo>); |
| |
| /// The layout strategy manages a model of the layout that is shared with the |
| /// Presenter through the TileLayoutModel. |
| class DejaLayout extends Layout<TileLayoutModel> { |
| var _tilerModel = TilerModel<ModuleInfo>(); |
| List<TilerModel<ModuleInfo>> _tilerModelSuggestions = []; |
| final _connections = <String, ChildViewConnection>{}; |
| final _layoutPolicy = LayoutPolicy(layoutStore: LayoutStore()); |
| |
| /// Tiling layout presenter |
| TilePresenter presenter; |
| |
| /// Construct a Layout Manage that uses the Deja layout algorithm. |
| /// Deja stores past layouts and matches them for the |
| /// new layout when views are added or removed. |
| DejaLayout({ |
| RemoveSurfaceCallback removeSurface, |
| FocusChangeCallback changeFocus, |
| }) : super( |
| removeSurface: removeSurface, |
| changeFocus: changeFocus, |
| ) { |
| presenter = TilePresenter( |
| removeSurfaceCallback: removeSurface, |
| changeFocusCallback: changeFocus, |
| requestLayoutCallback: _userLayoutRequest); |
| } |
| |
| /// Called by Modular |
| @override |
| void addSurface({ |
| String surfaceId, |
| String intent, |
| ChildViewConnection view, |
| UnmodifiableListView<String> parameters, |
| }) { |
| final content = ModuleInfo( |
| modName: surfaceId, |
| intent: intent, |
| parameters: parameters, |
| ); |
| |
| // Find the largest tile in the _tilerModel and have our content split |
| // and insert itself there. |
| splitLargestContent(_tilerModel, content); |
| final tilerModels = _layoutPolicy.getLayout(_tilerModel); |
| _tilerModel = tilerModels.first; |
| _tilerModelSuggestions = tilerModels.take(4).toList(); |
| _connections[surfaceId] = view; |
| _onLayoutChange(); |
| presenter.onSuggestionChange(_tilerModelSuggestions); |
| } |
| |
| /// Called by Modular |
| @override |
| void deleteSurface(String surfaceId) { |
| final tile = findContent(_tilerModel, surfaceId); |
| if (tile != null) { |
| _tilerModel.remove(tile); |
| } |
| _connections.remove(surfaceId); |
| // Regenerate Layout Suggestions. |
| final tilerModels = _layoutPolicy.getLayout(_tilerModel); |
| _tilerModel = tilerModels.first; |
| _tilerModelSuggestions = tilerModels.take(4).toList(); |
| _onLayoutChange(); |
| presenter.onSuggestionChange(_tilerModelSuggestions); |
| } |
| |
| /// A utility to send the layout to the presenter |
| void _onLayoutChange() { |
| presenter.onLayoutChange(TileLayoutModel( |
| model: _tilerModel, connections: BuiltMap(_connections))); |
| } |
| |
| /// Presenter is asking that this new layout be made active. This follows some |
| /// user editing or re-arranging. |
| void _userLayoutRequest(TilerModel<ModuleInfo> model) { |
| final modsToRemove = getModsDifference(_tilerModel, model); |
| _tilerModel = model; |
| removeSurface(modsToRemove); |
| if (modsToRemove.isNotEmpty) { |
| // Regenerate Layout Suggestions. |
| final tilerModels = _layoutPolicy.getLayout(_tilerModel); |
| _tilerModel = tilerModels.first; |
| _tilerModelSuggestions = tilerModels.take(4).toList(); |
| presenter.onSuggestionChange(_tilerModelSuggestions); |
| } |
| _onLayoutChange(); |
| _layoutPolicy.write(_tilerModel); |
| } |
| } |