| // 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:async'; |
| import 'dart:collection'; |
| import 'package:flutter/foundation.dart'; |
| import '../blocs/webpage_bloc.dart'; |
| import '../models/tabs_action.dart'; |
| |
| // Business logic for browser tabs. |
| // Sinks: |
| // TabsAction: a tabs action - open new tab, focus tab, etc. |
| // Value Notifiers: |
| // Tabs: the list of open tabs. |
| // CurrentTab: the currently focused tab. |
| class TabsBloc { |
| final _tabsList = <WebPageBloc>[]; |
| final WebPageBloc Function() tabFactory; |
| final void Function(WebPageBloc tab) disposeTab; |
| |
| // Value Notifiers |
| final ValueNotifier<UnmodifiableListView<WebPageBloc>> _tabs = |
| ValueNotifier<UnmodifiableListView<WebPageBloc>>( |
| UnmodifiableListView(<WebPageBloc>[])); |
| final ValueNotifier<WebPageBloc?> _currentTab = |
| ValueNotifier<WebPageBloc?>(null); |
| |
| ChangeNotifier get tabsNotifier => _tabs; |
| ChangeNotifier get currentTabNotifier => _currentTab; |
| |
| UnmodifiableListView<WebPageBloc> get tabs => _tabs.value; |
| WebPageBloc? get currentTab => _currentTab.value; |
| int? get currentTabIdx => |
| (currentTab != null) ? _tabsList.indexOf(currentTab!) : null; |
| bool get isOnlyTab => _tabsList.length == 1; |
| |
| WebPageBloc? get previousTab { |
| final idx = currentTabIdx; |
| if (idx == null) { |
| return null; |
| } |
| int prevIdx = idx - 1; |
| prevIdx = (prevIdx < 0) ? (_tabsList.length - 1) : prevIdx; |
| return _tabsList[prevIdx]; |
| } |
| |
| WebPageBloc? get nextTab { |
| final idx = currentTabIdx; |
| if (idx == null) { |
| return null; |
| } |
| int nextIdx = idx + 1; |
| nextIdx = (nextIdx > _tabsList.length - 1) ? 0 : nextIdx; |
| return _tabsList[nextIdx]; |
| } |
| |
| // Sinks |
| final _tabsActionController = StreamController<TabsAction>(); |
| Sink<TabsAction> get request => _tabsActionController.sink; |
| |
| TabsBloc({required this.tabFactory, required this.disposeTab}) { |
| _tabsActionController.stream.listen(_onTabsActionChanged); |
| } |
| |
| void dispose() { |
| _tabsList.forEach(disposeTab); |
| _tabsActionController.close(); |
| } |
| |
| void _onTabsActionChanged(TabsAction action) { |
| switch (action.op) { |
| case TabsActionType.newTab: |
| final tab = tabFactory(); |
| _tabsList.add(tab); |
| _tabs.value = UnmodifiableListView<WebPageBloc>(_tabsList); |
| _currentTab.value = tab; |
| break; |
| case TabsActionType.focusTab: |
| final focusTab = action as FocusTabAction; |
| _currentTab.value = focusTab.tab; |
| break; |
| case TabsActionType.removeTab: |
| if (tabs.isEmpty) { |
| break; |
| } |
| |
| final removeTab = action as RemoveTabAction; |
| final tab = removeTab.tab; |
| |
| if (tabs.length == 1) { |
| _currentTab.value = null; |
| } else if (currentTab == removeTab.tab) { |
| final indexOfRemoved = _tabsList.indexOf(tab); |
| final indexOfNewTab = |
| indexOfRemoved == 0 ? indexOfRemoved + 1 : indexOfRemoved - 1; |
| _currentTab.value = _tabsList.elementAt(indexOfNewTab); |
| } |
| |
| _tabsList.remove(tab); |
| _tabs.value = UnmodifiableListView<WebPageBloc>(_tabsList); |
| disposeTab(tab); |
| break; |
| case TabsActionType.addTab: |
| final addTabAction = action as AddTabAction; |
| _tabsList.add(addTabAction.tab); |
| _tabs.value = UnmodifiableListView<WebPageBloc>(_tabsList); |
| _currentTab.value = addTabAction.tab; |
| break; |
| case TabsActionType.rearrangeTabs: |
| final rearrangeTabs = action as RearrangeTabsAction; |
| final originalIndex = rearrangeTabs.originalIndex; |
| final newIndex = rearrangeTabs.newIndex; |
| |
| final movingTab = _tabsList.elementAt(originalIndex); |
| // the tab moves to the left. |
| if (originalIndex > newIndex) { |
| _tabsList |
| ..removeAt(originalIndex) |
| ..insert(newIndex, movingTab); |
| } |
| // the tab moves to the right. |
| else { |
| _tabsList |
| ..insert(newIndex + 1, movingTab) |
| ..removeAt(originalIndex); |
| } |
| _tabs.value = UnmodifiableListView<WebPageBloc>(_tabsList); |
| break; |
| } |
| } |
| } |