blob: 7768780408df70f45cdb63de456a540ee6dad0dd [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 '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;
}
}
}