Skip to content

Commit

Permalink
Add Right-to-Left support to line painting (#31)
Browse files Browse the repository at this point in the history
Author: https://github.com/naory159

* add rtl support

* add direction to settings_view, fix rounded corners rtl issue
  • Loading branch information
naory159 authored Aug 9, 2022
1 parent cf5338e commit 233cfc1
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 29 deletions.
53 changes: 53 additions & 0 deletions example/lib/src/settings/_direction_selector.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
part of 'settings_view.dart';

class _DirectionSelector extends StatelessWidget {
const _DirectionSelector({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
final appController = AppController.of(context);

return ValueListenableBuilder<TreeViewTheme>(
valueListenable: appController.treeViewTheme,
builder: (_, TreeViewTheme theme, __) {
final selectedDirection = theme.direction;

void changeDirection(TextDirection direction) {
if (direction == selectedDirection) return;
appController.updateTheme(
theme.copyWith(direction: direction),
);
}

return _SettingsButtonBar(
label: 'Direction',
buttonBarPadding: const EdgeInsets.symmetric(horizontal: 24),
children: [
TextButton(
onPressed: () => changeDirection(TextDirection.ltr),
child: Text(
'Left to Right',
style: TextStyle(
color: selectedDirection == TextDirection.ltr
? kDarkBlue
: Colors.grey,
),
),
),
TextButton(
onPressed: () => changeDirection(TextDirection.rtl),
child: Text(
'Right to Left',
style: TextStyle(
color: selectedDirection == TextDirection.rtl
? kDarkBlue
: Colors.grey,
),
),
),
],
);
},
);
}
}
3 changes: 3 additions & 0 deletions example/lib/src/settings/settings_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ part '_line_style_selector.dart';
part '_line_thickness_slider.dart';
part '_swap_expansion_button.dart';
part '_toggle_rounded_corners_button.dart';
part '_direction_selector.dart';

const Duration kAnimationDuration = Duration(milliseconds: 300);

Expand Down Expand Up @@ -55,6 +56,8 @@ class SettingsView extends StatelessWidget {
SizedBox(height: 10),
_IndentationSlider(),
SizedBox(height: 10),
_DirectionSelector(),
SizedBox(height: 10),
_SettingsHeader(text: 'Other'),
_SwapExpansionButton(),
],
Expand Down
25 changes: 19 additions & 6 deletions lib/src/lines_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ enum TreeLine {
/// draw the lines that compose a single node in the [TreeView] widget.
class LinesPainter extends CustomPainter {
/// Creates a [LinesPainter].
LinesPainter({required this.theme, required this.linesToBeDrawn});
LinesPainter({
required this.theme,
required this.linesToBeDrawn,
});

/// The theme to use to draw the lines.
final TreeViewTheme theme;
Expand All @@ -47,6 +50,7 @@ class LinesPainter extends CustomPainter {
index: index,
lineCount: linesToBeDrawn.length,
roundLineCorners: theme.roundLineCorners,
direction: theme.direction,
);

canvas.drawPath(offset.draw(linesToBeDrawn[index]), paint);
Expand All @@ -67,6 +71,7 @@ class _LineOffset {
required this.index,
required this.lineCount,
required this.roundLineCorners,
required this.direction,
});
final double height;
final double width;
Expand All @@ -75,6 +80,8 @@ class _LineOffset {

final bool roundLineCorners;

final TextDirection direction;

late final double xStart = width * index;

late final double xEnd = width * lineCount;
Expand All @@ -101,6 +108,8 @@ class _LineOffset {
/// */
/// ```
late final double oneQuarterDiffFromRight = xEnd - ((xEnd - centerX) * 0.5);
late final double oneQuarterDiffFromLeft =
xStart - ((xStart - centerX) * 0.5);

Path draw(TreeLine line) {
switch (line) {
Expand Down Expand Up @@ -130,15 +139,17 @@ class _LineOffset {
..quadraticBezierTo(
centerX,
centerY,
oneQuarterDiffFromRight,
direction == TextDirection.ltr
? oneQuarterDiffFromRight
: oneQuarterDiffFromLeft,
centerY,
)
..lineTo(xEnd, centerY);
..lineTo(direction == TextDirection.ltr ? xEnd : xStart, centerY);
} else {
path.lineTo(centerX, centerY);
}

path.lineTo(xEnd, centerY);
path.lineTo(direction == TextDirection.ltr ? xEnd : xStart, centerY);

return path;
}
Expand All @@ -154,14 +165,16 @@ class _LineOffset {
..quadraticBezierTo(
centerX,
centerY,
oneQuarterDiffFromRight,
direction == TextDirection.ltr
? oneQuarterDiffFromRight
: oneQuarterDiffFromLeft,
centerY,
);
} else {
path.moveTo(centerX, centerY);
}

path.lineTo(xEnd, centerY);
path.lineTo(direction == TextDirection.ltr ? xEnd : xStart, centerY);

return path;
}
Expand Down
4 changes: 3 additions & 1 deletion lib/src/lines_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ class LinesWidget extends StatelessWidget {
case LineStyle.connected:
return CustomPaint(
painter: LinesPainter(
linesToBeDrawn: treeNodeScope.node.connectedLines,
linesToBeDrawn: treeNodeScope.node.connectedLines(
treeNodeScope.theme.direction,
),
theme: treeNodeScope.theme,
),
child: child,
Expand Down
30 changes: 21 additions & 9 deletions lib/src/tree_node.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:collection' show UnmodifiableSetView;

import 'package:flutter/material.dart';

import 'lines_painter.dart' show TreeLine;

/// This class represents one node in the Tree.
Expand Down Expand Up @@ -239,20 +241,30 @@ extension TreeNodeX on TreeNode {

/// A list of [TreeLine]s that defines how connected lines will be drawn
/// when [TreeViewTheme.lineStyle] is set to [LineStyle.connected].
List<TreeLine> get connectedLines {
List<TreeLine> connectedLines(TextDirection direction) {
if (isRoot || isMostTopLevel) return const [];

if (depth == 1) return [prefixLine];

final parentLines = parent!.connectedLines;
final parentLines = parent!.connectedLines(direction);

return [
// Copy parent lines, except the last one.
...parentLines.sublist(0, parentLines.length - 1),
// Swap the last line of parent to connect or not to siblings.
lastParentLineEquivalent(parentLines.last),
prefixLine,
];
if (direction == TextDirection.ltr) {
return [
// Copy parent lines, except the last one.
...parentLines.sublist(0, parentLines.length - 1),
// Swap the last line of parent to connect or not to siblings.
lastParentLineEquivalent(parentLines.last),
prefixLine,
];
} else {
return [
prefixLine,
// Swap the last line of parent to connect or not to siblings.
lastParentLineEquivalent(parentLines.last),
// Copy parent lines, except the first one.
...parentLines.sublist(1, parentLines.length),
];
}
}

/// A list of [TreeLine] that defines how scoped lines will be drawn
Expand Down
28 changes: 16 additions & 12 deletions lib/src/tree_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,18 +149,22 @@ class _TreeViewState extends State<TreeView> {
return _TreeViewScope(
controller: controller,
theme: widget.theme,
child: ListView.custom(
controller: widget.scrollController,
padding: widget.padding,
shrinkWrap: widget.shrinkWrap,
itemExtent: widget.nodeHeight,
childrenDelegate: SliverChildBuilderDelegate(
_nodeBuilder,
childCount: controller.visibleNodes.length,
findChildIndexCallback: (Key key) {
final index = controller.indexOf((key as ValueKey<TreeNode>).value);
return index < 0 ? null : index;
},
child: Directionality(
textDirection: widget.theme.direction,
child: ListView.custom(
controller: widget.scrollController,
padding: widget.padding,
shrinkWrap: widget.shrinkWrap,
itemExtent: widget.nodeHeight,
childrenDelegate: SliverChildBuilderDelegate(
_nodeBuilder,
childCount: controller.visibleNodes.length,
findChildIndexCallback: (Key key) {
final index =
controller.indexOf((key as ValueKey<TreeNode>).value);
return index < 0 ? null : index;
},
),
),
),
);
Expand Down
8 changes: 7 additions & 1 deletion lib/src/tree_view_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class TreeViewTheme {
this.lineThickness = 2.0,
this.indent = 40.0,
this.roundLineCorners = false,
this.direction = TextDirection.ltr,
}) : assert(
indent >= lineThickness,
'The indent must not be less than lineThickness',
Expand Down Expand Up @@ -74,6 +75,8 @@ class TreeViewTheme {
/// Defaults to `false`.
final bool roundLineCorners;

final TextDirection direction;

@override
int get hashCode => hashValues(
lineStyle,
Expand All @@ -91,7 +94,8 @@ class TreeViewTheme {
lineStyle == other.lineStyle &&
lineThickness == other.lineThickness &&
indent == other.indent &&
roundLineCorners == other.roundLineCorners;
roundLineCorners == other.roundLineCorners &&
direction == other.direction;
}

/// Returns a copy of this object with new attributes.
Expand All @@ -101,13 +105,15 @@ class TreeViewTheme {
double? lineThickness,
double? indent,
bool? roundLineCorners,
TextDirection? direction,
}) {
return TreeViewTheme(
lineColor: lineColor ?? this.lineColor,
lineStyle: lineStyle ?? this.lineStyle,
lineThickness: lineThickness ?? this.lineThickness,
indent: indent ?? this.indent,
roundLineCorners: roundLineCorners ?? this.roundLineCorners,
direction: direction ?? this.direction,
);
}
}

0 comments on commit 233cfc1

Please sign in to comment.