Skip to content

Commit 38b3ba3

Browse files
committed
Ship Viewer now has three options for showing spoilers
1 parent a7086b9 commit 38b3ba3

File tree

10 files changed

+156
-25
lines changed

10 files changed

+156
-25
lines changed

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
- Export grid data (mods, ships, weapons) to csv.
1313
- You may now set custom paths for more game folders: executable, mods, saves, and game data (core).
1414
- NOT FINISHED in prerelease version.
15+
- Ship Viewer now has three options for showing spoilers; none, "show CODEX_UNLOCKABLE", and show all spoilers including <ultra redacted>.
1516
- Fixed
1617
- Ship Viewer filters not working.
1718
- Custom launcher files are no longer hardcoded to use the game folder as their working directory.
1819
- This fixes Fast Rendering, which launches using a .bat in the `starsector-core` folder.
1920
- Changed
2021
- Cleaned up search bar positioning on Ships and Weapons pages.
22+
- CODEX_UNLOCKABLE ships are now hidden by default on the Ship Viewer.
2123

2224
# 1.2.3
2325
- Fixed

lib/catalog/mod_browser_page.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import 'package:trios/widgets/tristate_icon_button.dart';
2424

2525
import '../main.dart';
2626
import '../trios/download_manager/downloader.dart';
27-
import '../widgets/MultiSplitViewMixin.dart';
27+
import '../widgets/multi_split_mixin_view.dart';
2828
import '../widgets/moving_tooltip.dart';
2929
import 'mod_browser_manager.dart';
3030

lib/mod_manager/homebrew_grid/wispgrid_header_row_view.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import 'package:trios/mod_manager/homebrew_grid/wispgrid_group.dart';
1010
import 'package:trios/thirdparty/dartx/function.dart';
1111
import 'package:trios/thirdparty/flutter_context_menu/flutter_context_menu.dart';
1212
import 'package:trios/utils/extensions.dart';
13-
import 'package:trios/widgets/MultiSplitViewMixin.dart';
13+
import 'package:trios/widgets/multi_split_mixin_view.dart';
1414
import 'package:trios/widgets/hoverable_widget.dart';
1515
import 'package:trios/widgets/moving_tooltip.dart';
1616

lib/portraits/portraits_page.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import 'package:trios/trios/constants.dart';
1818
import 'package:trios/utils/extensions.dart';
1919
import 'package:trios/utils/logging.dart';
2020
import 'package:trios/utils/search.dart';
21-
import 'package:trios/widgets/MultiSplitViewMixin.dart';
21+
import 'package:trios/widgets/multi_split_mixin_view.dart';
2222
import 'package:trios/widgets/blur.dart';
2323
import 'package:trios/widgets/expanding_constrained_aligned_widget.dart';
2424
import 'package:trios/widgets/moving_tooltip.dart';

lib/shipViewer/ships_page.dart

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:trios/shipViewer/filter_widget.dart';
1111
import 'package:trios/shipViewer/models/shipGpt.dart';
1212
import 'package:trios/shipViewer/ship_manager.dart';
1313
import 'package:trios/shipViewer/ships_page_controller.dart';
14+
import 'package:trios/themes/theme.dart';
1415
import 'package:trios/themes/theme_manager.dart' show ThemeManager;
1516
import 'package:trios/thirdparty/flutter_context_menu/flutter_context_menu.dart';
1617
import 'package:trios/trios/app_state.dart';
@@ -24,8 +25,9 @@ import 'package:trios/widgets/export_to_csv_dialog.dart';
2425
import 'package:trios/widgets/moving_tooltip.dart';
2526
import 'package:trios/widgets/text_trios.dart';
2627
import 'package:trios/widgets/toolbar_checkbox_button.dart';
28+
import 'package:trios/widgets/trios_dropdown_menu.dart';
2729

28-
import '../widgets/MultiSplitViewMixin.dart';
30+
import '../widgets/multi_split_mixin_view.dart';
2931

3032
class ShipsPage extends ConsumerStatefulWidget {
3133
const ShipsPage({super.key});
@@ -151,15 +153,41 @@ class _ShipsPageState extends ConsumerState<ShipsPage>
151153
),
152154
),
153155
const SizedBox(width: 8),
154-
MovingTooltipWidget.text(
155-
message:
156-
"Show ships with 'HIDE_IN_CODEX' or certain ultra-redacted vanilla tags.",
157-
warningLevel: TooltipWarningLevel.error,
158-
child: TriOSToolbarCheckboxButton(
159-
text: "Show Spoilers",
160-
value: controllerState.showSpoilers,
161-
onChanged: (value) => controller.toggleShowSpoilers(),
162-
),
156+
TriOSDropdownMenu<SpoilerLevel>(
157+
initialSelection: controllerState.spoilerLevelToShow,
158+
onSelected: (level) {
159+
if (level == null) return;
160+
controller.setShowSpoilers(level);
161+
},
162+
dropdownMenuEntries: [
163+
DropdownMenuEntry(
164+
value: SpoilerLevel.showNone,
165+
label: "No Spoilers",
166+
labelWidget: MovingTooltipWidget.text(
167+
message: "No spoilers shown at all.",
168+
child: Text("No Spoilers"),
169+
),
170+
),
171+
DropdownMenuEntry(
172+
value: SpoilerLevel.showSlightSpoilers,
173+
label: "Slight spoilers",
174+
labelWidget: MovingTooltipWidget.text(
175+
warningLevel: TooltipWarningLevel.warning,
176+
message: "Shows CODEX_UNLOCKABLE ships.",
177+
child: Text("Slight spoilers"),
178+
),
179+
),
180+
DropdownMenuEntry(
181+
value: SpoilerLevel.showAllSpoilers,
182+
label: "Show all spoilers",
183+
labelWidget: MovingTooltipWidget.text(
184+
warningLevel: TooltipWarningLevel.error,
185+
message:
186+
"Show all spoilers, including HIDE_IN_CODEX and certain ultra-redacted vanilla tagged ships",
187+
child: Text("Show all spoilers"),
188+
),
189+
),
190+
],
163191
),
164192
const SizedBox(width: 8),
165193
TriOSToolbarCheckboxButton(

lib/shipViewer/ships_page_controller.dart

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import 'package:trios/utils/extensions.dart';
1515
/// State class for the ships page controller
1616
class ShipsPageState {
1717
final bool showEnabled;
18-
final bool showSpoilers;
18+
final SpoilerLevel spoilerLevelToShow;
1919
final bool splitPane;
2020
final bool showFilters;
2121
final List<GridFilter<Ship>> filterCategories;
@@ -31,7 +31,7 @@ class ShipsPageState {
3131

3232
const ShipsPageState({
3333
this.showEnabled = false,
34-
this.showSpoilers = false,
34+
this.spoilerLevelToShow = SpoilerLevel.showNone,
3535
this.splitPane = false,
3636
this.showFilters = false,
3737
this.filterCategories = const [],
@@ -46,7 +46,7 @@ class ShipsPageState {
4646

4747
ShipsPageState copyWith({
4848
bool? showEnabled,
49-
bool? showSpoilers,
49+
SpoilerLevel? spoilerLevelToShow,
5050
bool? splitPane,
5151
bool? showFilters,
5252
List<GridFilter<Ship>>? filterCategories,
@@ -60,7 +60,7 @@ class ShipsPageState {
6060
}) {
6161
return ShipsPageState(
6262
showEnabled: showEnabled ?? this.showEnabled,
63-
showSpoilers: showSpoilers ?? this.showSpoilers,
63+
spoilerLevelToShow: spoilerLevelToShow ?? this.spoilerLevelToShow,
6464
splitPane: splitPane ?? this.splitPane,
6565
showFilters: showFilters ?? this.showFilters,
6666
filterCategories: filterCategories ?? this.filterCategories,
@@ -76,8 +76,11 @@ class ShipsPageState {
7676
}
7777
}
7878

79+
enum SpoilerLevel { showNone, showSlightSpoilers, showAllSpoilers }
80+
7981
/// Controller for the ships page using AutoDisposeNotifier (synchronous)
8082
class ShipsPageController extends AutoDisposeNotifier<ShipsPageState> {
83+
final slightSpoilerTags = ["codex_unlockable"];
8184
final spoilerTags = ["threat", "dweller"];
8285

8386
@override
@@ -185,7 +188,7 @@ class ShipsPageController extends AutoDisposeNotifier<ShipsPageState> {
185188
ships = _filterByEnabled(ships, mods, currentState.showEnabled);
186189

187190
// Apply spoiler filter
188-
ships = _filterBySpoilers(ships, currentState.showSpoilers);
191+
ships = _filterBySpoilers(ships, currentState.spoilerLevelToShow);
189192

190193
// Store ships before grid filters for filter panel
191194
final shipsBeforeGridFilter = ships.toList();
@@ -225,9 +228,9 @@ class ShipsPageController extends AutoDisposeNotifier<ShipsPageState> {
225228
}
226229

227230
/// Toggle show spoilers filter
228-
void toggleShowSpoilers() {
231+
void setShowSpoilers(SpoilerLevel spoilerLevelToShow) {
229232
final mods = ref.read(AppState.mods);
230-
final updatedState = state.copyWith(showSpoilers: !state.showSpoilers);
233+
final updatedState = state.copyWith(spoilerLevelToShow: spoilerLevelToShow);
231234
final processedState = _processAllFilters(updatedState, mods);
232235

233236
state = processedState;
@@ -301,17 +304,26 @@ class ShipsPageController extends AutoDisposeNotifier<ShipsPageState> {
301304
}
302305

303306
/// Filter ships based on spoiler settings
304-
List<Ship> _filterBySpoilers(List<Ship> ships, bool showSpoilers) {
305-
if (showSpoilers) return ships;
307+
List<Ship> _filterBySpoilers(
308+
List<Ship> ships,
309+
SpoilerLevel spoilerLevelToShow,
310+
) {
311+
if (spoilerLevelToShow == SpoilerLevel.showAllSpoilers) return ships;
306312

307313
return ships.where((ship) {
308314
final hints = ship.hints.orEmpty().map((h) => h.toLowerCase());
309315
final tags = ship.tags.orEmpty().map((t) => t.toLowerCase());
310316

311317
final hidden = hints.contains('hide_in_codex');
318+
final isSlightSpoiler = tags.any(slightSpoilerTags.contains);
312319
final isSpoiler = tags.any(spoilerTags.contains);
313320

314-
return !hidden && !isSpoiler;
321+
if (spoilerLevelToShow == SpoilerLevel.showSlightSpoilers) {
322+
return !hidden && !isSpoiler;
323+
}
324+
325+
// Show no spoilers
326+
return !hidden && !isSlightSpoiler && !isSpoiler;
315327
}).toList();
316328
}
317329

lib/weaponViewer/weapons_page.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import 'package:trios/widgets/moving_tooltip.dart';
2828
import 'package:trios/widgets/text_trios.dart';
2929
import 'package:trios/widgets/toolbar_checkbox_button.dart';
3030

31-
import '../widgets/MultiSplitViewMixin.dart';
31+
import '../widgets/multi_split_mixin_view.dart';
3232

3333
class WeaponsPage extends ConsumerStatefulWidget {
3434
const WeaponsPage({super.key});

lib/widgets/checkbox_with_label.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:flutter/material.dart';
2+
import 'package:trios/themes/theme_manager.dart';
23
import 'package:trios/widgets/blur.dart';
34
import 'package:trios/widgets/conditional_wrap.dart';
45

@@ -88,7 +89,7 @@ class CheckboxWithLabel extends StatelessWidget {
8889
onChanged(!value!);
8990
}
9091
},
91-
borderRadius: BorderRadius.circular(14),
92+
borderRadius: BorderRadius.circular(ThemeManager.cornerRadius),
9293
child: Padding(
9394
padding: const EdgeInsets.only(right: 7),
9495
child: Row(
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import 'package:flutter/material.dart';
2+
3+
/// A themed, generic dropdown menu used across TriOS.
4+
/// Mirrors the compact toolbar style used in the app (dense, rounded, no filter/search).
5+
class TriOSDropdownMenu<T> extends StatelessWidget {
6+
final T? initialSelection;
7+
final ValueChanged<T?>? onSelected;
8+
final List<DropdownMenuEntry<T>> dropdownMenuEntries;
9+
10+
// Optional overrides
11+
final double height;
12+
final double borderRadius;
13+
final EdgeInsetsGeometry contentPadding;
14+
final TextStyle? textStyle;
15+
final bool enabled;
16+
17+
const TriOSDropdownMenu({
18+
super.key,
19+
required this.dropdownMenuEntries,
20+
this.initialSelection,
21+
this.onSelected,
22+
this.height = 30,
23+
this.borderRadius = 10,
24+
this.contentPadding = const EdgeInsets.symmetric(horizontal: 16),
25+
this.textStyle,
26+
this.enabled = true,
27+
});
28+
29+
@override
30+
Widget build(BuildContext context) {
31+
final theme = Theme.of(context);
32+
33+
return DropdownMenu<T>(
34+
enabled: enabled,
35+
initialSelection: initialSelection,
36+
onSelected: onSelected,
37+
enableFilter: false,
38+
enableSearch: false,
39+
requestFocusOnTap: false,
40+
textStyle: textStyle ?? theme.textTheme.labelLarge,
41+
trailingIcon: Transform.translate(
42+
offset: const Offset(3, -6),
43+
child: const Icon(Icons.arrow_drop_down),
44+
),
45+
selectedTrailingIcon: Transform.translate(
46+
offset: const Offset(3, -6),
47+
child: const Icon(Icons.arrow_drop_up),
48+
),
49+
inputDecorationTheme: InputDecorationTheme(
50+
isDense: true,
51+
contentPadding: contentPadding,
52+
constraints: BoxConstraints.tight(Size.fromHeight(height)),
53+
enabledBorder: OutlineInputBorder(
54+
borderRadius: BorderRadius.circular(borderRadius),
55+
borderSide: BorderSide(color: theme.colorScheme.outlineVariant),
56+
),
57+
disabledBorder: OutlineInputBorder(
58+
borderRadius: BorderRadius.circular(borderRadius),
59+
borderSide: BorderSide(
60+
color: theme.colorScheme.outlineVariant.withOpacity(0.4),
61+
),
62+
),
63+
focusedBorder: OutlineInputBorder(
64+
borderRadius: BorderRadius.circular(borderRadius),
65+
borderSide: BorderSide(color: theme.colorScheme.primary),
66+
),
67+
),
68+
dropdownMenuEntries: dropdownMenuEntries
69+
.map(
70+
(e) => DropdownMenuEntry<T>(
71+
value: e.value,
72+
label: e.label,
73+
labelWidget: e.labelWidget,
74+
leadingIcon: e.leadingIcon,
75+
// Keep per-entry text style aligned with the menu style by default
76+
style:
77+
e.style ??
78+
ButtonStyle(
79+
textStyle: WidgetStatePropertyAll(
80+
textStyle ?? theme.textTheme.labelLarge,
81+
),
82+
),
83+
),
84+
)
85+
.toList(),
86+
);
87+
}
88+
}

0 commit comments

Comments
 (0)