Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions lib/const/coordinate_system.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ class CoordinateSystem {
isScreenshot = value;
}

static CoordinateSystem? _instance;
/// The coordinates of the canvas on screen, set by that canvas as it lays
/// out. A screen without a canvas must not set them: every item, lineup
/// line and stroke on the canvas converts through them. Until a canvas has
/// laid out, the capture size stands in, so a strategy can load before its
/// canvas is on screen.
static CoordinateSystem _instance =
CoordinateSystem._(playAreaSize: screenShotSize);

// Use screenshot dimensions when isScreenshot is true; otherwise use play area size
Size get _effectiveSize => isScreenshot ? screenShotSize : _playAreaSize;
Expand Down Expand Up @@ -100,7 +106,7 @@ class CoordinateSystem {

factory CoordinateSystem({required Size playAreaSize}) {
_instance = CoordinateSystem._(playAreaSize: playAreaSize);
return _instance!;
return _instance;
}

/// The coordinates every capture draws in: 1920×1080, always in screenshot
Expand Down Expand Up @@ -133,12 +139,7 @@ class CoordinateSystem {
_lastZoneIsCapture = zone[_captureZoneKey] is CoordinateSystem;
}
if (_lastZoneIsCapture) return _capture;
if (_instance == null) {
throw StateError(
"CoordinateSystem must be initialized with playAreaSize first",
);
}
return _instance!;
return _instance;
}

Offset screenToCoordinate(Offset screenPoint) {
Expand Down
4 changes: 0 additions & 4 deletions lib/widgets/folder_navigator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:icarus/config/platform_policy.dart';
import 'package:icarus/const/maps.dart';
import 'package:icarus/const/coordinate_system.dart';
import 'package:icarus/const/settings.dart';
import 'package:icarus/const/update_checker.dart';
import 'package:icarus/main.dart';
Expand Down Expand Up @@ -203,9 +202,6 @@ class _FolderNavigatorState extends ConsumerState<FolderNavigator> {

final desktopUpdateController = ref.watch(desktopUpdateControllerProvider);

final double height = MediaQuery.sizeOf(context).height - 90;
final Size playAreaSize = Size(height * (16 / 9), height);
CoordinateSystem(playAreaSize: playAreaSize);
final tab = ref.watch(libraryTabProvider);
final workspace = ref.watch(libraryWorkspaceProvider);
final isCloudWorkspace = workspace == LibraryWorkspace.cloud;
Expand Down
166 changes: 166 additions & 0 deletions test/library_under_editor_coordinates_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hive_ce_flutter/adapters.dart';
import 'package:icarus/collab/cloud_library_models.dart';
import 'package:icarus/config/platform_policy.dart';
import 'package:icarus/const/coordinate_system.dart';
import 'package:icarus/const/hive_boxes.dart';
import 'package:icarus/const/maps.dart';
import 'package:icarus/const/traversal_speed.dart';
import 'package:icarus/const/update_checker.dart';
import 'package:icarus/domain/folder.dart';
import 'package:icarus/hive/hive_registration.dart';
import 'package:icarus/interactive_map.dart';
import 'package:icarus/providers/auth_provider.dart';
import 'package:icarus/providers/collab/remote_library_provider.dart';
import 'package:icarus/providers/desktop_update_provider.dart';
import 'package:icarus/providers/drawing_provider.dart';
import 'package:icarus/providers/map_provider.dart';
import 'package:icarus/providers/pen_provider.dart';
import 'package:icarus/providers/update_status_provider.dart';
import 'package:icarus/providers/user_preferences_provider.dart';
import 'package:icarus/providers/welcome_tour_provider.dart';
import 'package:icarus/strategy/strategy_models.dart';
import 'package:icarus/widgets/folder_navigator.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:toastification/toastification.dart';

class _SignedOut extends AuthProvider {
@override
AppAuthState build() => AppAuthState.fromSession(null);
}

class _FixedMapProvider extends MapProvider {
@override
MapState build() => MapState(currentMap: MapValue.ascent, isAttack: true);
}

class _EmptyDrawingProvider extends DrawingProvider {
@override
DrawingState build() => DrawingState(elements: const []);

@override
void rebuildAllPaths(CoordinateSystem coordinateSystem) {}
}

class _FixedPenProvider extends PenProvider {
@override
PenState build() => PenState(
listOfColors: const [],
color: Colors.white,
hasArrow: false,
isDotted: false,
opacity: 1,
thickness: 1,
penMode: PenMode.freeDraw,
traversalTimeEnabled: false,
activeTraversalSpeedProfile: TraversalSpeedProfile.running,
drawingCursor: null,
erasingCursor: null,
);
}

/// The editor's geometry: StrategyView's 40px window strip over the canvas.
class _Editor extends StatelessWidget {
const _Editor();

@override
Widget build(BuildContext context) => const Scaffold(
body: Column(
children: [
SizedBox(height: 40),
Expanded(child: InteractiveMap()),
],
),
);
}

/// The editor opens on top of the library, so the library stays mounted
/// underneath and rebuilds whenever something it watches changes: a cloud
/// folder list update, a token refresh. Every placed item, lineup line and
/// stroke converts through [CoordinateSystem.instance]; a rebuild there must
/// not change the size they convert with.
void main() {
late Directory tempDir;

setUpAll(() => registerIcarusAdapters(Hive));

setUp(() async {
tempDir = await Directory.systemTemp.createTemp('icarus-coordinates-');
Hive.init(tempDir.path);
await Hive.openBox<StrategyData>(HiveBoxNames.strategiesBox);
await Hive.openBox<Folder>(HiveBoxNames.foldersBox);
await Hive.openBox<int>(HiveBoxNames.pinnedItemsBox);
await Hive.openBox<dynamic>(HiveBoxNames.strategyOutboxBox);
await Hive.openBox<dynamic>(HiveBoxNames.cloudMediaOutboxBox);
await Hive.openBox<MapThemeProfile>(HiveBoxNames.mapThemeProfilesBox);
await Hive.openBox<AppPreferences>(HiveBoxNames.appPreferencesBox);
await MapThemeProfilesProvider.bootstrap();
});

tearDown(() async {
await Hive.close();
await tempDir.delete(recursive: true);
});

testWidgets('the library rebuilding under the editor keeps its coordinates',
(tester) async {
tester.view.physicalSize = const Size(1600, 900);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);

await tester.pumpWidget(
ProviderScope(
overrides: [
platformPolicyProvider.overrideWithValue(PlatformPolicy.desktop),
authProvider.overrideWith(_SignedOut.new),
appUpdateStatusProvider.overrideWith(
(ref) async => const UpdateCheckResult(
isSupported: false,
isUpdateAvailable: false,
source: 'test',
),
),
desktopUpdateControllerProvider.overrideWithValue(null),
welcomeTourLaunchCheckedProvider.overrideWith((ref) => true),
cloudFolderTreeProvider.overrideWith(
(_) => Stream.value(const <CloudFolderEntry>[]),
),
cloudStrategiesProvider.overrideWith((_) => Stream.value(const [])),
mapProvider.overrideWith(_FixedMapProvider.new),
drawingProvider.overrideWith(_EmptyDrawingProvider.new),
penProvider.overrideWith(_FixedPenProvider.new),
effectiveMapThemePaletteProvider.overrideWith(
(ref) => MapThemeProfilesProvider.immutableDefaultPalette,
),
],
child: const ToastificationWrapper(
child: ShadApp(home: FolderNavigator()),
),
),
);
await tester.pumpAndSettle();

final navigator = tester.state<NavigatorState>(find.byType(Navigator));
navigator.push(MaterialPageRoute<void>(builder: (_) => const _Editor()));
await tester.pumpAndSettle();

final canvas = tester.getSize(find.byType(InteractiveMap));
final editorSize = Size(canvas.height * 16 / 9, canvas.height);
expect(CoordinateSystem.instance.playAreaSize, editorSize);

tester
.element(find.byType(FolderNavigator, skipOffstage: false))
.markNeedsBuild();
await tester.pump();

expect(CoordinateSystem.instance.playAreaSize, editorSize);

navigator.pop();
await tester.pumpAndSettle();
});
}
Loading