4444import javafx .scene .text .Text ;
4545import javafx .scene .text .TextFlow ;
4646import javafx .stage .FileChooser ;
47- import org .jackhuang .hmcl .mod .LocalModFile ;
4847import org .jackhuang .hmcl .mod .ModLoaderType ;
4948import org .jackhuang .hmcl .mod .RemoteMod ;
5049import org .jackhuang .hmcl .mod .modrinth .ModrinthRemoteModRepository ;
6059import org .jackhuang .hmcl .ui .SVG ;
6160import org .jackhuang .hmcl .ui .construct .*;
6261import org .jackhuang .hmcl .ui .nbt .NBTEditorPage ;
62+ import org .jackhuang .hmcl .util .Pair ;
6363import org .jackhuang .hmcl .util .StringUtils ;
6464import org .jackhuang .hmcl .util .i18n .I18n ;
6565import org .jackhuang .hmcl .util .io .FileUtils ;
7373import java .nio .file .NoSuchFileException ;
7474import java .nio .file .Path ;
7575import java .util .*;
76- import java .util .stream .Collectors ;
7776import java .util .stream .Stream ;
7877
7978import static org .jackhuang .hmcl .ui .FXUtils .ignoreEvent ;
8079import static org .jackhuang .hmcl .ui .FXUtils .onEscPressed ;
8180import static org .jackhuang .hmcl .ui .ToolbarListPageSkin .createToolbarButton2 ;
81+ import static org .jackhuang .hmcl .util .Pair .pair ;
8282import static org .jackhuang .hmcl .util .i18n .I18n .i18n ;
8383import static org .jackhuang .hmcl .util .logging .Logger .LOG ;
8484
@@ -105,6 +105,7 @@ private static String translateType(SchematicType type) {
105105 private String instanceId ;
106106 private Path schematicsDirectory ;
107107 private final ObjectProperty <DirItem > currentDirectory = new SimpleObjectProperty <>(this , "currentDirectory" , null );
108+ private final ObjectProperty <Pair <String , Runnable >> warningTip = new SimpleObjectProperty <>(this , "tip" , pair (null , null ));
108109
109110 private final BooleanProperty isRootProperty = new SimpleBooleanProperty (this , "isRoot" , true );
110111
@@ -149,20 +150,24 @@ public void refresh() {
149150 setLoading (true );
150151 var modManager = profile .getRepository ().getModManager (instanceId );
151152 Task .supplyAsync (() -> {
152- boolean hasLitematica = false ;
153+ LitematicaState litematicaState = LitematicaState . NOT_INSTALLED ;
153154 try {
154155 modManager .refreshMods ();
155- var set = modManager .getMods ()
156- .stream ()
157- .map (LocalModFile ::getId )
158- .collect (Collectors .toSet ());
159- if (set .contains ("litematica" ) || set .contains ("forgematica" )) {
160- hasLitematica = true ;
156+ var mods = modManager .getMods ();
157+ for (var localModFile : mods ) {
158+ if ("litematica" .equals (localModFile .getId ()) || "forgematica" .equals (localModFile .getId ())) {
159+ if (localModFile .isActive ()) {
160+ litematicaState = LitematicaState .OK ;
161+ break ;
162+ } else {
163+ litematicaState = LitematicaState .DISABLED ;
164+ }
165+ }
161166 }
162167 } catch (IOException e ) {
163168 LOG .warning ("Failed to load mods, unable to check litematica" , e );
164169 }
165- if (! hasLitematica ) {
170+ if (litematicaState == LitematicaState . NOT_INSTALLED ) {
166171 try {
167172 if (litematica == null ) litematica = ModrinthRemoteModRepository .MODS .getModById ("litematica" );
168173 } catch (IOException ignored ) {
@@ -171,50 +176,48 @@ public void refresh() {
171176 if (forgematica == null ) forgematica = ModrinthRemoteModRepository .MODS .getModById ("forgematica" );
172177 } catch (IOException ignored ) {
173178 }
174- return null ;
175179 }
176- return loadRoot (schematicsDirectory );
180+ return pair ( litematicaState , loadRoot (schematicsDirectory ) );
177181 }).whenComplete (Schedulers .javafx (), (result , exception ) -> {
178182 if (exception == null ) {
179- if (result == null ) {
180- boolean useForgematica = forgematica != null
181- && ( modManager . getSupportedLoaders (). contains ( ModLoaderType . FORGE ) || modManager . getSupportedLoaders (). contains ( ModLoaderType . NEO_FORGED ))
182- && GameVersionNumber . asGameVersion ( Optional . ofNullable ( modManager .getGameVersion ())). isAtLeast ( "1.16.4" , "20w45a" );
183- if ( useForgematica || litematica != null ) {
184- setFailedReason ( i18n ( "schematics.no_litematica_install" ));
185- setOnFailedAction ( __ -> {
186- var modDownloads = Controllers .getDownloadPage ().showModDownloads ();
187- modDownloads .selectVersion (instanceId );
188- Controllers .navigate (new DownloadPage (
189- modDownloads ,
190- useForgematica ? forgematica : litematica ,
191- modDownloads .getProfileVersion (),
192- modDownloads .getCallback ())
193- );
194- } );
195- } else {
196- setFailedReason ( i18n ("schematics.no_litematica" ));
197- setOnFailedAction ( null );
183+ switch (result . key () ) {
184+ case NOT_INSTALLED -> {
185+ boolean useForgematica = forgematica != null
186+ && ( modManager . getSupportedLoaders (). contains ( ModLoaderType . FORGE ) || modManager .getSupportedLoaders (). contains ( ModLoaderType . NEO_FORGED ))
187+ && GameVersionNumber . asGameVersion ( Optional . ofNullable ( modManager . getGameVersion ())). isAtLeast ( "1.16.4" , "20w45a" );
188+ if ( useForgematica || litematica != null ) {
189+ warningTip . set ( pair ( i18n ( "schematics.warning.no_litematica_install" ), () -> {
190+ var modDownloads = Controllers .getDownloadPage ().showModDownloads ();
191+ modDownloads .selectVersion (instanceId );
192+ Controllers .navigate (new DownloadPage (
193+ modDownloads ,
194+ useForgematica ? forgematica : litematica ,
195+ modDownloads .getProfileVersion (),
196+ modDownloads .getCallback ())
197+ );
198+ }) );
199+ } else {
200+ warningTip . set ( pair ( i18n ("schematics.warning. no_litematica" ), null ));
201+ }
198202 }
199- } else {
200- DirItem target = result ;
201- if (currentDirectoryProperty ().get () != null ) {
202- loop :
203- for (String dirName : currentDirectoryProperty ().get ().relativePath ) {
204- target .preLoad ();
205- for (var dirChild : target .dirChildren ) {
206- if (dirChild .getName ().equals (dirName )) {
207- target = dirChild ;
208- continue loop ;
209- }
203+ case DISABLED -> warningTip .set (pair (i18n ("schematics.warning.litematica_disabled" ), null ));
204+ default -> warningTip .set (pair (null , null ));
205+ }
206+ DirItem target = result .value ();
207+ if (currentDirectoryProperty ().get () != null ) {
208+ loop :
209+ for (String dirName : currentDirectoryProperty ().get ().relativePath ) {
210+ target .preLoad ();
211+ for (var dirChild : target .dirChildren ) {
212+ if (dirChild .getName ().equals (dirName )) {
213+ target = dirChild ;
214+ continue loop ;
210215 }
211- break ;
212216 }
217+ break ;
213218 }
214-
215- setFailedReason (null );
216- navigateTo (target );
217219 }
220+ navigateTo (target );
218221 setLoading (false );
219222 } else {
220223 LOG .warning ("Failed to load schematics" , exception );
@@ -802,13 +805,28 @@ private static final class SchematicsPageSkin extends SkinBase<SchematicsPage> {
802805 root .getContent ().add (toolbar );
803806 }
804807
808+ {
809+ var tip = new TextFlow ();
810+ tip .setStyle ("-fx-font-size: 13;" );
811+ HBox .setMargin (tip , new Insets (5 ));
812+ var tipPane = new HBox (tip );
813+ tipPane .setAlignment (Pos .CENTER_LEFT );
814+ skinnable .warningTip .addListener ((observable , oldValue , newValue ) -> {
815+ root .getContent ().remove (tipPane );
816+ if (newValue != null && !StringUtils .isBlank (newValue .key ())) {
817+ var txt = new Text (newValue .key ());
818+ if (newValue .value () != null ) FXUtils .onClicked (txt , newValue .value ());
819+ tip .getChildren ().setAll (txt );
820+ root .getContent ().add (1 , tipPane );
821+ }
822+ });
823+ }
824+
805825 {
806826 SpinnerPane center = new SpinnerPane ();
807827 ComponentList .setVgrow (center , Priority .ALWAYS );
808828 center .getStyleClass ().add ("large-spinner-pane" );
809829 center .loadingProperty ().bind (skinnable .loadingProperty ());
810- center .failedReasonProperty ().bind (skinnable .failedReasonProperty ());
811- center .onFailedActionProperty ().bind (skinnable .onFailedActionProperty ());
812830
813831 listView .setCellFactory (x -> new Cell (listView ));
814832 listView .setSelectionModel (new NoneMultipleSelectionModel <>());
@@ -847,4 +865,11 @@ private static final class SchematicsPageSkin extends SkinBase<SchematicsPage> {
847865 getChildren ().setAll (pane );
848866 }
849867 }
868+
869+ private enum LitematicaState {
870+ DISABLED ,
871+ NOT_INSTALLED ,
872+ OK
873+ }
874+
850875}
0 commit comments