forked from eva/focus-flow
fix: pack visually colliding timeline cards
This commit is contained in:
parent
acee1da264
commit
dd6552e68d
2 changed files with 50 additions and 15 deletions
|
|
@ -73,7 +73,10 @@ class _TimelineViewState extends State<TimelineView> {
|
||||||
minCardHeight: 88,
|
minCardHeight: 88,
|
||||||
);
|
);
|
||||||
final contentHeight = geometry.totalHeight + 24;
|
final contentHeight = geometry.totalHeight + 24;
|
||||||
final placements = _TimelineCardPlacement.pack(widget.cards);
|
final placements = _TimelineCardPlacement.pack(
|
||||||
|
widget.cards,
|
||||||
|
geometry: geometry,
|
||||||
|
);
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
_scheduleInitialScroll(geometry, constraints.maxHeight);
|
_scheduleInitialScroll(geometry, constraints.maxHeight);
|
||||||
|
|
@ -195,7 +198,10 @@ class _TimelineCardPlacement {
|
||||||
final int lane;
|
final int lane;
|
||||||
final int laneCount;
|
final int laneCount;
|
||||||
|
|
||||||
static List<_TimelineCardPlacement> pack(List<TimelineCardModel> cards) {
|
static List<_TimelineCardPlacement> pack(
|
||||||
|
List<TimelineCardModel> cards, {
|
||||||
|
required TimelineGeometry geometry,
|
||||||
|
}) {
|
||||||
if (cards.isEmpty) {
|
if (cards.isEmpty) {
|
||||||
return const [];
|
return const [];
|
||||||
}
|
}
|
||||||
|
|
@ -213,41 +219,50 @@ class _TimelineCardPlacement {
|
||||||
});
|
});
|
||||||
final placements = <_TimelineCardPlacement>[];
|
final placements = <_TimelineCardPlacement>[];
|
||||||
var groupStart = 0;
|
var groupStart = 0;
|
||||||
var groupEnd = sorted.first.endMinutes;
|
var groupEnd = _visualEnd(sorted.first, geometry);
|
||||||
for (var index = 1; index < sorted.length; index += 1) {
|
for (var index = 1; index < sorted.length; index += 1) {
|
||||||
final card = sorted[index];
|
final card = sorted[index];
|
||||||
if (card.startMinutes < groupEnd) {
|
final cardStart = _visualStart(card, geometry);
|
||||||
if (card.endMinutes > groupEnd) {
|
final cardEnd = _visualEnd(card, geometry);
|
||||||
groupEnd = card.endMinutes;
|
if (cardStart < groupEnd) {
|
||||||
|
if (cardEnd > groupEnd) {
|
||||||
|
groupEnd = cardEnd;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
placements.addAll(_packGroup(sorted.sublist(groupStart, index)));
|
placements.addAll(
|
||||||
|
_packGroup(sorted.sublist(groupStart, index), geometry: geometry),
|
||||||
|
);
|
||||||
groupStart = index;
|
groupStart = index;
|
||||||
groupEnd = card.endMinutes;
|
groupEnd = cardEnd;
|
||||||
}
|
}
|
||||||
placements.addAll(_packGroup(sorted.sublist(groupStart)));
|
placements.addAll(
|
||||||
|
_packGroup(sorted.sublist(groupStart), geometry: geometry),
|
||||||
|
);
|
||||||
return placements;
|
return placements;
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<_TimelineCardPlacement> _packGroup(
|
static List<_TimelineCardPlacement> _packGroup(
|
||||||
List<TimelineCardModel> group,
|
List<TimelineCardModel> group, {
|
||||||
) {
|
required TimelineGeometry geometry,
|
||||||
final laneEnds = <int>[];
|
}) {
|
||||||
|
final laneEnds = <double>[];
|
||||||
final laneByCard = <TimelineCardModel, int>{};
|
final laneByCard = <TimelineCardModel, int>{};
|
||||||
for (final card in group) {
|
for (final card in group) {
|
||||||
|
final cardStart = _visualStart(card, geometry);
|
||||||
|
final cardEnd = _visualEnd(card, geometry);
|
||||||
var assignedLane = -1;
|
var assignedLane = -1;
|
||||||
for (var lane = 0; lane < laneEnds.length; lane += 1) {
|
for (var lane = 0; lane < laneEnds.length; lane += 1) {
|
||||||
if (laneEnds[lane] <= card.startMinutes) {
|
if (laneEnds[lane] <= cardStart) {
|
||||||
assignedLane = lane;
|
assignedLane = lane;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (assignedLane == -1) {
|
if (assignedLane == -1) {
|
||||||
assignedLane = laneEnds.length;
|
assignedLane = laneEnds.length;
|
||||||
laneEnds.add(card.endMinutes);
|
laneEnds.add(cardEnd);
|
||||||
} else {
|
} else {
|
||||||
laneEnds[assignedLane] = card.endMinutes;
|
laneEnds[assignedLane] = cardEnd;
|
||||||
}
|
}
|
||||||
laneByCard[card] = assignedLane;
|
laneByCard[card] = assignedLane;
|
||||||
}
|
}
|
||||||
|
|
@ -262,6 +277,18 @@ class _TimelineCardPlacement {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double _visualStart(
|
||||||
|
TimelineCardModel card,
|
||||||
|
TimelineGeometry geometry,
|
||||||
|
) {
|
||||||
|
return geometry.yForMinutesSinceMidnight(card.startMinutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static double _visualEnd(TimelineCardModel card, TimelineGeometry geometry) {
|
||||||
|
final duration = card.endMinutes - card.startMinutes;
|
||||||
|
return _visualStart(card, geometry) + geometry.heightForDuration(duration);
|
||||||
|
}
|
||||||
|
|
||||||
double left(double trackWidth) {
|
double left(double trackWidth) {
|
||||||
final laneWidth = _laneWidth(trackWidth);
|
final laneWidth = _laneWidth(trackWidth);
|
||||||
final laneGap = _laneGapForWidth(trackWidth);
|
final laneGap = _laneGapForWidth(trackWidth);
|
||||||
|
|
|
||||||
|
|
@ -242,6 +242,12 @@ void main() {
|
||||||
final waterPlants = tester.getRect(
|
final waterPlants = tester.getRect(
|
||||||
find.byKey(const ValueKey('water-plants')),
|
find.byKey(const ValueKey('water-plants')),
|
||||||
);
|
);
|
||||||
|
final cleanCoffee = tester.getRect(
|
||||||
|
find.byKey(const ValueKey('clean-coffee-maker')),
|
||||||
|
);
|
||||||
|
final cleanedKitchenEarly = tester.getRect(
|
||||||
|
find.byKey(const ValueKey('cleaned-kitchen-counter-early')),
|
||||||
|
);
|
||||||
|
|
||||||
expect(startLaundry.left, greaterThan(sortMail.left));
|
expect(startLaundry.left, greaterThan(sortMail.left));
|
||||||
expect(waterPlants.left, closeTo(sortMail.left, 1));
|
expect(waterPlants.left, closeTo(sortMail.left, 1));
|
||||||
|
|
@ -249,6 +255,8 @@ void main() {
|
||||||
expect(waterPlants.width, closeTo(sortMail.width, 1));
|
expect(waterPlants.width, closeTo(sortMail.width, 1));
|
||||||
expect(sortMail.right, lessThan(startLaundry.left));
|
expect(sortMail.right, lessThan(startLaundry.left));
|
||||||
expect(waterPlants.right, lessThan(startLaundry.left));
|
expect(waterPlants.right, lessThan(startLaundry.left));
|
||||||
|
expect(cleanedKitchenEarly.left, greaterThan(cleanCoffee.left));
|
||||||
|
expect(cleanCoffee.right, lessThan(cleanedKitchenEarly.left));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('top bar controls scale inside a compact header width', (
|
testWidgets('top bar controls scale inside a compact header width', (
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue