Commit 74761265 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Add dartdoc for tabs and text selection handles (#4715)

parent 4be730ac
......@@ -519,9 +519,15 @@ class TabBarSelection<T> extends StatefulWidget {
}
}
/// State for a [TabBarSelection] widget.
///
/// Subclasses of [TabBarSelection] typically use [State] objects that extend
/// this class.
class TabBarSelectionState<T> extends State<TabBarSelection<T>> {
/// An animation that updates as the selected tab changes.
Animation<double> get animation => _controller.view;
// Both the TabBar and TabBarView classes access _controller because they
// alternately drive selection progress between tabs.
final AnimationController _controller = new AnimationController(duration: _kTabBarScroll, value: 1.0);
......@@ -559,18 +565,38 @@ class TabBarSelectionState<T> extends State<TabBarSelection<T>> {
PageStorage.of(context)?.writeState(context, _value);
}
/// The list of possible values that the selection can obtain.
List<T> get values => config.values;
/// The previously selected value.
///
/// When the tab selection changes, the tab selection animates from the
/// previously selected value to the new value.
T get previousValue => _previousValue;
T _previousValue;
bool _valueIsChanging = false;
/// Whether the tab selection is in the process of animating from one value to
/// another.
// TODO(abarth): Try computing this value from _controller.state so we don't
// need to keep a separate bool in sync.
bool get valueIsChanging => _valueIsChanging;
bool _valueIsChanging = false;
/// The index of a given value in [values].
///
/// Runs in constant time.
int indexOf(T tabValue) => _valueToIndex[tabValue];
/// The index of the currently selected value.
int get index => _valueToIndex[value];
/// The index of the previoulsy selected value.
int get previousIndex => indexOf(_previousValue);
/// The currently selected value.
///
/// Writing to this field will cause the tab selection to animate from the
/// previous value to the new value.
T get value => _value;
T _value;
set value(T newValue) {
......@@ -607,6 +633,8 @@ class TabBarSelectionState<T> extends State<TabBarSelection<T>> {
_controller
..value = value
..forward().then((_) {
// TODO(abarth): Consider using a status listener and checking for
// AnimationStatus.completed.
if (_controller.value == 1.0) {
if (config.onChanged != null)
config.onChanged(_value);
......@@ -617,6 +645,9 @@ class TabBarSelectionState<T> extends State<TabBarSelection<T>> {
final List<TabBarSelectionAnimationListener> _animationListeners = <TabBarSelectionAnimationListener>[];
/// Calls listener methods every time the value or status of the selection animation changes.
///
/// Listeners can be removed with [unregisterAnimationListener].
void registerAnimationListener(TabBarSelectionAnimationListener listener) {
_animationListeners.add(listener);
_controller
......@@ -624,6 +655,9 @@ class TabBarSelectionState<T> extends State<TabBarSelection<T>> {
..addListener(listener.handleProgressChange);
}
/// Stop calling listener methods every time the value or status of the animation changes.
///
/// Listeners can be added with [registerAnimationListener].
void unregisterAnimationListener(TabBarSelectionAnimationListener listener) {
_animationListeners.remove(listener);
_controller
......@@ -649,7 +683,7 @@ class TabBarSelectionState<T> extends State<TabBarSelection<T>> {
}
}
/// Displays a horizontal row of tabs, one per label.
/// A widget that displays a horizontal row of tabs, one per label.
///
/// Requires one of its ancestors to be a [TabBarSelection] widget to enable
/// saving and monitoring the selected tab.
......@@ -663,15 +697,23 @@ class TabBarSelectionState<T> extends State<TabBarSelection<T>> {
/// * [AppBar.tabBar]
/// * <https://www.google.com/design/spec/components/tabs.html>
class TabBar<T> extends Scrollable implements AppBarBottomWidget {
/// Creates a widget that displays a horizontal row of tabs, one per label.
///
/// The [labels] argument must not be null.
TabBar({
Key key,
this.labels,
@required this.labels,
this.isScrollable: false,
this.indicatorColor,
this.labelColor
}) : super(key: key, scrollDirection: Axis.horizontal);
}) : super(key: key, scrollDirection: Axis.horizontal) {
assert(labels != null);
}
/// The labels to display in the tabs.
///
/// The [TabBarSelection.values] are used as keys for this map to determine
/// which tab label is selected.
final Map<T, TabLabel> labels;
/// Whether this tab bar can be scrolled horizontally.
......@@ -978,10 +1020,23 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
}
}
/// A widget that displays the contents of a tab.
///
/// Requires one of its ancestors to be a [TabBarSelection] widget to enable
/// saving and monitoring the selected tab.
///
/// See also:
///
/// * [TabBarSelection]
/// * [TabBar]
/// * <https://www.google.com/design/spec/components/tabs.html>
class TabBarView<T> extends PageableList {
/// Creates a widget that displays the contents of a tab.
///
/// The [children] argument must not be null and must not be empty.
TabBarView({
Key key,
List<Widget> children
@required List<Widget> children
}) : super(
key: key,
scrollDirection: Axis.horizontal,
......@@ -1165,7 +1220,20 @@ class _TabBarViewState<T> extends PageableListState<TabBarView<T>> implements Ta
}
}
/// A widget that displays a visual indicator of which tab is selected.
///
/// Requires one of its ancestors to be a [TabBarSelection] widget to enable
/// saving and monitoring the selected tab.
///
/// See also:
///
/// * [TabBarSelection]
/// * [TabBarView]
class TabPageSelector<T> extends StatelessWidget {
/// Creates a widget that displays a visual indicator of which tab is selected.
///
/// Requires one of its ancestors to be a [TabBarSelection] widget to enable
/// saving and monitoring the selected tab.
const TabPageSelector({ Key key }) : super(key: key);
Widget _buildTabIndicator(TabBarSelectionState<T> selection, T tab, Animation<double> animation, ColorTween selectedColor, ColorTween previousColor) {
......
......@@ -381,6 +381,22 @@ abstract class SchedulerBinding extends BindingBase {
Duration _epochStart = Duration.ZERO;
Duration _lastRawTimeStamp = Duration.ZERO;
/// Prepares the scheduler for a non-monotonic change to how time stamps are calcuated.
///
/// Callbacks received from the scheduler assume that their time stamps are
/// monotonically increasing. The raw time stamp passed to [handleBeginFrame]
/// is monotonic, but the scheduler might adjust those time stamps to provide
/// [timeDilation]. Without careful handling, these adjusts could cause time
/// to appear to run backwards.
///
/// The [resetEpoch] function ensures that the time stamps are monotonic by
/// reseting the base time stamp used for future time stamp adjustments to the
/// current value. For example, if the [timeDilation] decreases, rather than
/// scaling down the [Duration] since the beginning of time, [resetEpoch] will
/// ensure that we only scale down the duration since [resetEpoch] was called.
///
/// Note: Setting [timeDilation] calls [resetEpoch] automatically. You don't
/// need to call [resetEpoch] yourself.
void resetEpoch() {
_epochStart = _adjustForEpoch(_lastRawTimeStamp);
_firstRawTimeStampInEpoch = null;
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:flutter/rendering.dart';
import 'package:meta/meta.dart';
import 'basic.dart';
import 'container.dart';
......@@ -12,21 +13,36 @@ import 'gesture_detector.dart';
import 'overlay.dart';
// TODO(mpcomplete): Need one for [collapsed].
/// Which type of selection handle to be displayed. With mixed-direction text,
/// both handles may be the same type. Examples:
/// LTR text: 'the <quick brown> fox'
/// Which type of selection handle to be displayed.
///
/// With mixed-direction text, both handles may be the same type. Examples:
///
/// * LTR text: 'the <quick brown> fox':
/// The '<' is drawn with the [left] type, the '>' with the [right]
/// RTL text: 'xof <nworb kciuq> eht'
///
/// * RTL text: 'xof <nworb kciuq> eht':
/// Same as above.
/// mixed text: '<the nwor<b quick fox'
///
/// * mixed text: '<the nwor<b quick fox'
/// Here 'the b' is selected, but 'brown' is RTL. Both are drawn with the
/// [left] type.
enum TextSelectionHandleType { left, right, collapsed }
enum TextSelectionHandleType {
/// The selection handle is to the left of the selection end point.
left,
/// Builds a handle of the given type.
/// The selection handle is to the right of the selection end point.
right,
/// The start and end of the selection are co-incident at this point.
collapsed,
}
/// Builds a selection handle of the given type.
typedef Widget TextSelectionHandleBuilder(BuildContext context, TextSelectionHandleType type);
// Builds a copy/paste toolbar.
/// Builds a tool bar near a text selection.
///
/// Typically displays buttons for copying and pasting text.
// TODO(mpcomplete): A single position is probably insufficient.
typedef Widget TextSelectionToolbarBuilder(BuildContext context, Point position, TextSelectionDelegate delegate);
......@@ -47,27 +63,57 @@ abstract class TextSelectionDelegate {
void hideToolbar();
}
/// Manages a pair of text selection handles to be shown in an Overlay
/// containing the owning widget.
/// An object that manages a pair of text selection handles.
///
/// The selection handles are displayed in the [Overlay] that most closely
/// encloses the given [BuildContext].
class TextSelectionOverlay implements TextSelectionDelegate {
/// Creates an object that manages overly entries for selection handles.
///
/// The [context] must not be null and must have an [Overlay] as an ancestor.
TextSelectionOverlay({
InputValue input,
this.context,
@required this.context,
this.debugRequiredFor,
this.renderObject,
this.onSelectionOverlayChanged,
this.handleBuilder,
this.toolbarBuilder
}): _input = input;
}): _input = input {
assert(context != null);
}
/// The context in which the selection handles should appear.
///
/// This context must have an [Overlay] as an ancestor because this object
/// will display the text selection handles in that [Overlay].
final BuildContext context;
/// Debugging information for explaining why the [Overlay] is required.
final Widget debugRequiredFor;
// TODO(mpcomplete): what if the renderObject is removed or replaced, or
// moves? Not sure what cases I need to handle, or how to handle them.
/// The editable line in which the selected text is being displayed.
final RenderEditableLine renderObject;
/// Called when the the selection changes.
///
/// For example, if the use drags one of the selection handles, this function
/// will be called with a new input value with an updated selection.
final ValueChanged<InputValue> onSelectionOverlayChanged;
/// Builds the selection handles.
///
/// The selection handles let the user adjust which portion of the text is
/// selected.
final TextSelectionHandleBuilder handleBuilder;
/// Builds a tool bar to display near the selection.
///
/// The tool bar typically contains buttons for copying and pasting text.
final TextSelectionToolbarBuilder toolbarBuilder;
InputValue _input;
/// A pair of handles. If this is non-null, there are always 2, though the
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment