Skip to content
11 changes: 11 additions & 0 deletions lib/data/color_scheme_extensions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:flutter/material.dart';

extension CounterSpellColorScheme on ColorScheme {
/// A darker shade of [primaryContainer], for surfaces that sit *behind* a
/// primaryContainer element — e.g. the +/- pill behind the pill editor's
/// highlighted center capsule.
Color get primaryContainerDim => Color.alphaBlend(
Colors.black.withValues(alpha: 0.35),
primaryContainer,
);
}
29 changes: 29 additions & 0 deletions lib/models/interaction/pill_focus.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'package:counter_spell/models/game/counter.dart';

sealed class PillFocus {
const PillFocus();
}

class CounterPillFocus extends PillFocus {
const CounterPillFocus(this.counter);
final Counter counter;

@override
bool operator ==(Object other) =>
other is CounterPillFocus && other.counter == counter;

@override
int get hashCode => counter.hashCode;
}

class CastPillFocus extends PillFocus {
const CastPillFocus({required this.partnerA});
final bool partnerA;

@override
bool operator ==(Object other) =>
other is CastPillFocus && other.partnerA == partnerA;

@override
int get hashCode => partnerA.hashCode;
}
8 changes: 8 additions & 0 deletions lib/widgets/arena/player_cell/arena_player_cell.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:counter_spell/main.dart';
import 'package:counter_spell/models/game/counter.dart';
import 'package:counter_spell/models/interaction/interaction_mode.dart';
import 'package:counter_spell/models/interaction/pill_focus.dart';
import 'package:counter_spell/widgets/arena/player_cell/components/player_cell_body.dart';
import 'package:counter_spell/widgets/arena/player_cell/components/player_cell_decoration.dart';
import 'package:counter_spell/widgets/arena/player_cell/components/player_cell_padding.dart';
Expand Down Expand Up @@ -61,11 +62,16 @@ class _ArenaPlayerCellState extends State<_ArenaPlayerCell>
@override
Reactive<int?> cachedAttackerIndex = Reactive(null);

/// which count pill has taken over the cell (null in the normal life view)
@override
Reactive<PillFocus?> focusedPill = Reactive(null);

@override
void dispose() {
increment.dispose();
advanced.dispose();
cachedAttackerIndex.dispose();
focusedPill.dispose();
super.dispose();
}

Expand Down Expand Up @@ -143,6 +149,8 @@ mixin ArenaPlayerController {

Reactive<int?> get cachedAttackerIndex;

Reactive<PillFocus?> get focusedPill;

void nextMultipleOf(int n);

void previousMultipleOf(int n);
Expand Down
138 changes: 84 additions & 54 deletions lib/widgets/arena/player_cell/components/player_cell_body.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:counter_spell/widgets/arena/player_cell/arena_player_cell.dart';
import 'package:counter_spell/widgets/arena/player_cell/components/player_cell_advanced_body.dart';
import 'package:counter_spell/widgets/arena/player_cell/components/player_cell_basic_body.dart';
import 'package:counter_spell/widgets/arena/player_cell/components/player_cell_more_button.dart';
import 'package:counter_spell/widgets/arena/player_cell/components/player_cell_pill_editor.dart';
import 'package:counter_spell/widgets/components/project/delay_provider.dart';
import 'package:flutter/material.dart';
import 'package:sid_base/sid_base.dart';
Expand Down Expand Up @@ -81,65 +82,94 @@ class _PlayerCellBodyState extends State<PlayerCellBody> {
alwaysScrollable: true,
);

return Stack(
children: [
Positioned.fill(
child: Align(
alignment: toTheLeft ? Alignment.centerLeft : Alignment.centerRight,
child: controller.advanced.buildWithStaticChild(
child: PageView(
physics: basicScrollPhysics,
children: [
PlayerCellBasicBody(
playerIndex: widget.playerIndex,
avoidMenuButton: widget.avoidMenuButton,
open: open,
),
],
),
builder: (context, value, child) => AnimatedListed(
listed: !value,
direction: Axis.horizontal,
axisAlignment: toTheLeft ? 1 : -1,
fadeFirstFraction: 1,
child: child,
return controller.focusedPill.build((context, focus) {
final bool editing = focus != null;
return Stack(
children: [
Positioned.fill(
child: AnimatedOpacity(
// same timing as the pill editor's entrance/exit
opacity: editing ? 0 : 1,
duration: Durations.medium4,
curve: Curves.easeOutCubic,
child: IgnorePointer(
ignoring: editing,
child: Stack(
children: [
Positioned.fill(
child: Align(
alignment: toTheLeft
? Alignment.centerLeft
: Alignment.centerRight,
child: controller.advanced.buildWithStaticChild(
child: PageView(
physics: basicScrollPhysics,
children: [
PlayerCellBasicBody(
playerIndex: widget.playerIndex,
avoidMenuButton: widget.avoidMenuButton,
open: open,
),
],
),
builder: (context, value, child) => AnimatedListed(
listed: !value,
direction: Axis.horizontal,
axisAlignment: toTheLeft ? 1 : -1,
fadeFirstFraction: 1,
child: child,
),
),
),
),
Positioned.fill(
child: Align(
alignment: toTheLeft
? Alignment.centerRight
: Alignment.centerLeft,
child: controller.advanced.buildWithStaticChild(
child: PlayerCellAdvancedBody(
playerIndex: widget.playerIndex,
onChangeOrientation: onChangeOrientation,
pageController: advancedPageController,
physics: advancedScrollPhysics,
),
builder: (context, value, child) => AnimatedListed(
listed: value,
direction: Axis.horizontal,
fadeFirstFraction: 1,
axisAlignment: toTheLeft ? -1 : 1,
child: child,
),
),
),
),
Positioned(
bottom: 0,
right: 0,
child: PlayerCellMoreButton(
open: () {
onChangeOrientation(true);
advancedPageController.jumpToPage(0);
open();
},
close: close,
),
),
],
),
),
),
),
),
Positioned.fill(
child: Align(
alignment: toTheLeft ? Alignment.centerRight : Alignment.centerLeft,
child: controller.advanced.buildWithStaticChild(
child: PlayerCellAdvancedBody(
if (focus != null)
Positioned.fill(
child: PlayerCellPillEditor(
playerIndex: widget.playerIndex,
onChangeOrientation: onChangeOrientation,
pageController: advancedPageController,
physics: advancedScrollPhysics,
),
builder: (context, value, child) => AnimatedListed(
listed: value,
direction: Axis.horizontal,
fadeFirstFraction: 1,
axisAlignment: toTheLeft ? -1 : 1,
child: child,
focus: focus,
),
),
),
),
Positioned(
bottom: 0,
right: 0,
child: PlayerCellMoreButton(
open: () {
onChangeOrientation(true);
advancedPageController.jumpToPage(0);
open();
},
close: close,
),
),
],
);
],
);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import 'package:auto_size_text/auto_size_text.dart';
import 'package:counter_spell/main.dart';
import 'package:counter_spell/models/game/partner_vectors.dart';
import 'package:counter_spell/models/game/player_state.dart';
import 'package:counter_spell/models/interaction/pill_focus.dart';
import 'package:counter_spell/models/pages.dart';
import 'package:counter_spell/widgets/arena/player_cell/arena_player_cell.dart';
import 'package:counter_spell/widgets/arena/player_cell/components/player_cell_icon_value.dart';
import 'package:flutter/material.dart';

Expand Down Expand Up @@ -31,13 +33,18 @@ class PlayerCellCastPage extends StatelessWidget {
times: -1,
),
),
onTap: () => counterSpell.gameLogic.editGame(
(game) => game.castCommander(
playerIndex: playerIndex,
partnerA: partnerA,
times: 1,
),
),
onTap: () {
counterSpell.gameLogic.editGame(
(game) => game.castCommander(
playerIndex: playerIndex,
partnerA: partnerA,
times: 1,
),
);
context.arenaPlayerController.focusedPill.update(
CastPillFocus(partnerA: partnerA),
);
},
child: PlayerCellIconValue(
icon: BodyPage.cast.filledIcon,
value: value == 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import 'package:auto_size_text/auto_size_text.dart';
import 'package:counter_spell/main.dart';
import 'package:counter_spell/models/game/counter.dart';
import 'package:counter_spell/models/game/player_state.dart';
import 'package:counter_spell/models/interaction/pill_focus.dart';
import 'package:counter_spell/widgets/arena/player_cell/arena_player_cell.dart';
import 'package:counter_spell/widgets/arena/player_cell/components/player_cell_icon_value.dart';
import 'package:flutter/material.dart';

Expand Down Expand Up @@ -32,17 +34,24 @@ class PlayerCellCounterPage extends StatelessWidget {
amount: -1,
),
),
onTap: () => counterSpell.gameLogic.editGame(
(game) => game.addCounters(
playerIndex: playerIndex,
counter: counter,
amount: isBoolean
? value == 0
? 1
: -1
: 1,
),
),
onTap: () {
counterSpell.gameLogic.editGame(
(game) => game.addCounters(
playerIndex: playerIndex,
counter: counter,
amount: isBoolean
? value == 0
? 1
: -1
: 1,
),
);
if (!isBoolean) {
context.arenaPlayerController.focusedPill.update(
CounterPillFocus(counter),
);
}
},
child: PlayerCellIconValue(
icon: counter.bigIcon,
highlightIcon: isBoolean && value > 0,
Expand Down
Loading