diff --git a/Readme.md b/Readme.md index 6a25fb6bb..0f95324ff 100644 --- a/Readme.md +++ b/Readme.md @@ -23,5 +23,5 @@ https://github.com/TheElectronWill/night-config SVG Salamander for SVG's\ https://github.com/blackears/svgSalamander -JavaFX for standalone jar and config ui\ -https://openjfx.io/ +FlatLaf for theming (for development testing, may remove later)\ +https://www.formdev.com/flatlaf/ diff --git a/core/src/main/java/com/seibel/lod/core/config/gui/ConfigScreen.java b/core/src/main/java/com/seibel/lod/core/config/gui/ConfigScreen.java index 54eee958d..4b6b3269b 100644 --- a/core/src/main/java/com/seibel/lod/core/config/gui/ConfigScreen.java +++ b/core/src/main/java/com/seibel/lod/core/config/gui/ConfigScreen.java @@ -1,42 +1,18 @@ package com.seibel.lod.core.config.gui; import javax.swing.*; -import javafx.application.Application; -import javafx.application.Platform; -import javafx.embed.swing.JFXPanel; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.scene.control.Label; -import javafx.scene.layout.StackPane; -import javafx.stage.Stage; - import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; public class ConfigScreen extends JFrame { - private final JFXPanel fxPanel = new JFXPanel(); public ConfigScreen() { -// super("JavaFX in Swing"); -// setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - setLayout(new BorderLayout()); - add(fxPanel, BorderLayout.CENTER); -// setSize(300, 200); -// setLocationRelativeTo(null); -// setVisible(true); - - Platform.runLater(() -> initFX(fxPanel)); - } - - private void initFX(JFXPanel fxPanel) { - Scene scene = createScene(); - fxPanel.setScene(scene); - } - - private Scene createScene() { - Label label = new Label("Hello from JavaFX!"); - return new Scene(label); + setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.weightx = 0.5; + constraints.gridx = 0; + constraints.gridy = 0; + add(new JLabel("Hello World!"), constraints); } diff --git a/core/src/main/java/com/seibel/lod/core/jar/JarMain.java b/core/src/main/java/com/seibel/lod/core/jar/JarMain.java index 6d2e606c5..4deb8ba5a 100644 --- a/core/src/main/java/com/seibel/lod/core/jar/JarMain.java +++ b/core/src/main/java/com/seibel/lod/core/jar/JarMain.java @@ -1,18 +1,16 @@ package com.seibel.lod.core.jar; +import com.formdev.flatlaf.FlatDarkLaf; +import com.formdev.flatlaf.FlatLightLaf; import com.seibel.lod.core.ModInfo; import com.seibel.lod.core.config.ConfigBase; import com.seibel.lod.core.jar.DarkModeDetector; import com.seibel.lod.core.jar.JarUtils; +import com.seibel.lod.core.jar.gui.BaseJFrame; import com.seibel.lod.core.jar.gui.cusomJObject.JBox; import com.seibel.lod.core.jar.installer.ModrinthGetter; import com.seibel.lod.core.jar.installer.WebDownloader; import com.seibel.lod.core.jar.JarDependencySetup; -import javafx.application.Application; -import javafx.scene.Scene; -import javafx.scene.control.Label; -import javafx.scene.layout.StackPane; -import javafx.stage.Stage; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -32,7 +30,7 @@ import java.util.concurrent.atomic.AtomicReference; * @author coolGi */ // Once built it would be in core/build/libs/DistantHorizons--dev-all.jar -public class JarMain extends Application { +public class JarMain { public static final Logger logger = LogManager.getLogger(JarMain.class.getSimpleName()); public static List programArgs; public static final boolean isDarkTheme = DarkModeDetector.isDarkMode(); @@ -41,15 +39,6 @@ public class JarMain extends Application { // TODO: Rewrite the standalone jar // Previous version here https://gitlab.com/jeseibel/distant-horizons-core/-/blob/333dc4d0e079777b712c0fff246837104ae9a2b6/core/src/main/java/com/seibel/lod/core/jar/JarMain.java - @Override - public void start(Stage stage) { - logger.debug("JavaFX version "+System.getProperty("javafx.version")); - - Label l = new Label("Hello, JavaFX "); - Scene scene = new Scene(new StackPane(l), 640, 480); - stage.setScene(scene); - stage.show(); - } public static void main(String[] args) { programArgs = Arrays.asList(args); @@ -77,8 +66,217 @@ public class JarMain extends Application { JarDependencySetup.createInitialBindings(); if (args.length == 0 || Arrays.asList(args).contains("--gui")) { - launch(args); + startGUI(); return; } } + + + + public static void startGUI() { + // Set up the theme +// System.setProperty("apple.awt.application.appearance", "system"); +// if (isDarkTheme) +// FlatDarkLaf.setup(); +// else +// FlatLightLaf.setup(); + // This is done in BaseJFrame now + + +// GitlabGetter.init(); + ModrinthGetter.init(); + System.out.println("WARNING: The standalone jar still work in progress"); + +// JOptionPane.showMessageDialog(null, "The GUI for the standalone jar isn't made yet\nIf you want to use the mod then put it in your mods folder", "Distant Horizons", JOptionPane.WARNING_MESSAGE); + +// if (getOperatingSystem().equals(OperatingSystem.MACOS)) { +// System.out.println("If you want the installer then please use Linux or Windows for the time being.\nMacOS support/testing will come later on"); +// } + + // Code will be changed later on to allow resizing and work better + + + + BaseJFrame frame = new BaseJFrame(false, true); + frame.addExtraButtons(frame.getWidth(), 0, true, false); + + // Buttons which you want to be stacked vertically should be added with this (`frame.add(obj, this);`) + GridBagConstraints verticalLayout = new GridBagConstraints(); + verticalLayout.gridy = GridBagConstraints.RELATIVE; + verticalLayout.gridx = 0; + verticalLayout.fill = GridBagConstraints.HORIZONTAL; + verticalLayout.weightx = 1.0; + verticalLayout.anchor = GridBagConstraints.NORTH; + + + // Selected download + AtomicReference downloadID = new AtomicReference(""); + + + // This is for the panel to show the update description + JPanel modVersionDescriptionPanel = new JPanel(new GridBagLayout()); + JScrollPane modVersionDescriptionScroll = new JScrollPane(modVersionDescriptionPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + // Sets all the layout stuff for it + int modDescriptionWidth = 275; + modVersionDescriptionScroll.setBounds(frame.getWidth()-modDescriptionWidth, 225, modDescriptionWidth, frame.getHeight()-255); + modVersionDescriptionScroll.setBorder(null); // Disables the border + modVersionDescriptionScroll.setWheelScrollingEnabled(true); + // The label + JLabel modVersionDescriptionLabel = new JLabel(); + modVersionDescriptionPanel.add(modVersionDescriptionLabel, verticalLayout); + // Finally add it + frame.add(modVersionDescriptionScroll); + + + + // This is for the pannel to select MinecraftVersion + JPanel modMinecraftVersionsPannel = new JPanel(new GridBagLayout()); + JScrollPane modMinecraftVersionsScroll = new JScrollPane(modMinecraftVersionsPannel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + // Sets all the layout stuff for it + modMinecraftVersionsScroll.setBounds(0, 225, 125, frame.getHeight()-255); + modMinecraftVersionsScroll.setBorder(null); // Disables the border + modMinecraftVersionsScroll.setWheelScrollingEnabled(true); + // List to store all the buttons + ArrayList modMinecraftReleaseButtons = new ArrayList<>(); + frame.add(modMinecraftVersionsScroll); + + + // This is for selecting the mod version + JPanel modVersionsPannel = new JPanel(new GridBagLayout()); + JScrollPane modVersionsScroll = new JScrollPane(modVersionsPannel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + // Sets all the layout stuff for it + modVersionsScroll.setBounds(125, 225, 100, frame.getHeight()-255); + modVersionsScroll.setBorder(null); // Disables the border + modVersionsScroll.setWheelScrollingEnabled(true); + // List to store all the buttons + ArrayList modReleaseButtons = new ArrayList<>(); + frame.add(modVersionsScroll); + + // Add all the buttons + for (String mcVer : ModrinthGetter.mcVersions) { + JButton btn = new JButton(mcVer); + btn.setBackground(UIManager.getColor("Panel.background")); // Does the same thing as removing the background + btn.setBorderPainted(false); // Removes the borders +// btn.setHorizontalAlignment(SwingConstants.LEFT); // Sets the text to be on the left side rather than the center + + btn.addActionListener(e -> { + // Clears the selected colors for the rest of the buttons + for (JButton currentBtn : modMinecraftReleaseButtons) + currentBtn.setBackground(UIManager.getColor("Panel.background")); + btn.setBackground(UIManager.getColor("Button.background")); // Sets this to the selected color + + // Clears the minecraft version panel + modVersionsPannel.removeAll(); + modReleaseButtons.clear(); + + + // Adds all the buttons for the minecraft panel + for (String modID : ModrinthGetter.mcVerToReleaseID.get(mcVer)) { + // No need to comment most of these as it is the same this as before + JButton btnDownload = new JButton(ModrinthGetter.releaseNames.get(modID)); + btnDownload.setBackground(UIManager.getColor("Panel.background")); + btnDownload.setBorderPainted(false); + btnDownload.setHorizontalAlignment(SwingConstants.LEFT); + + btnDownload.addActionListener(f -> { + downloadID.set(modID); + + for (JButton currentBtn : modReleaseButtons) + currentBtn.setBackground(UIManager.getColor("Panel.background")); + btnDownload.setBackground(UIManager.getColor("Button.background")); + + + modVersionDescriptionLabel.setText( + WebDownloader.formatMarkdownToHtml( + ModrinthGetter.changeLogs.get(modID), modDescriptionWidth-75) + ); + modVersionDescriptionPanel.repaint(); + }); + modVersionsPannel.add(btnDownload, verticalLayout); + modReleaseButtons.add(btnDownload); + } + + modVersionsScroll.getVerticalScrollBar().setValue(0); // Reset the scroll bar back to the top + + modVersionsPannel.repaint(); // Update the version pannel + frame.validate(); // Update the frame + }); + + modMinecraftVersionsPannel.add(btn, verticalLayout); + modMinecraftReleaseButtons.add(btn); + } + + + + // Bar at the top + frame.add(new JBox(UIManager.getColor("Separator.foreground"), 0, 220, frame.getWidth(), 5)); + // Minecraft version text + JLabel textMcVersionHeader = new JLabel("Minecraft version"); + textMcVersionHeader.setBounds(0, 200, 125, 20); + frame.add(textMcVersionHeader); + // Version text + JLabel textVersionHeader = new JLabel("Mod version"); + textVersionHeader.setBounds(125, 200, 150, 20); + frame.add(textVersionHeader); + + + + + // Stuff for setting the file install path + JFileChooser minecraftDirPop = new JFileChooser(); + switch (Platform.get()) { + case WINDOWS: + minecraftDirPop.setCurrentDirectory(new File(System.getenv("APPDATA") + "/.minecraft/mods")); + case LINUX: + minecraftDirPop.setCurrentDirectory(new File(System.getProperty("user.home") + "/.minecraft/mods")); + } + minecraftDirPop.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + JButton minecraftDirBtn = new JButton("Click to select install path"); + minecraftDirBtn.addActionListener(e -> { + if (minecraftDirPop.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) + minecraftDirBtn.setText(minecraftDirPop.getSelectedFile().toString()); + }); + minecraftDirBtn.setBounds(230, frame.getHeight()-100, 200, 20); + frame.add(minecraftDirBtn); + + // Button for the install button + JButton installMod = new JButton("Install " + ModInfo.READABLE_NAME); + installMod.setBounds(230, frame.getHeight()-70, 200, 20); + installMod.addActionListener(e -> { + if (minecraftDirPop.getSelectedFile() == null) { + JOptionPane.showMessageDialog(frame, "Please select your install directory", ModInfo.READABLE_NAME, JOptionPane.WARNING_MESSAGE); + return; + } + + URL downloadPath = ModrinthGetter.downloadUrl.get(downloadID.get()); + + try { + WebDownloader.downloadAsFile( + downloadPath, + minecraftDirPop.getSelectedFile().toPath().resolve( + ModInfo.NAME + "-" + ModrinthGetter.releaseNames.get(downloadID.get()) + ".jar" + ).toFile()); + + JOptionPane.showMessageDialog(frame, "Installation done. \nYou can now close the installer", ModInfo.READABLE_NAME, JOptionPane.INFORMATION_MESSAGE); + } catch (Exception f) { + JOptionPane.showMessageDialog(frame, "Download failed. Check your internet connection \nStacktrace: " + f.getMessage(), ModInfo.READABLE_NAME, JOptionPane.ERROR_MESSAGE); + } + }); + frame.add(installMod); + + + // Fabric installer +// try { +// WebDownloader.downloadAsFile(new URL("https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.11.0/fabric-installer-0.11.0.jar"), new File(System.getProperty("java.io.tmpdir") + "/fabricInstaller.jar")); +// Runtime.getRuntime().exec("java -jar " + System.getProperty("java.io.tmpdir") + "/fabricInstaller.jar"); +// } catch (Exception e) {e.printStackTrace();} + + + + + frame.addLogo(); // Has to be run at the end cus of a bug with java swing (it may not be a bug but idk how to fix it so I'll call it a bug) + + frame.validate(); // Update to add the widgets + frame.setVisible(true); // Start the ui + } } diff --git a/core/src/main/java/com/seibel/lod/core/jar/gui/BaseJFrame.java b/core/src/main/java/com/seibel/lod/core/jar/gui/BaseJFrame.java new file mode 100644 index 000000000..f9d43f671 --- /dev/null +++ b/core/src/main/java/com/seibel/lod/core/jar/gui/BaseJFrame.java @@ -0,0 +1,189 @@ +package com.seibel.lod.core.jar.gui; + +import com.formdev.flatlaf.FlatDarkLaf; +import com.formdev.flatlaf.FlatLightLaf; +import com.formdev.flatlaf.extras.FlatSVGIcon; +import com.seibel.lod.core.dependencyInjection.SingletonInjector; +import com.seibel.lod.core.jar.JarUtils; +import com.seibel.lod.core.wrapperInterfaces.config.ILangWrapper; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.List; + +/** + * @author coolGi + */ +// This will be removed later on to make a better ui +// To get colors use https://alvinalexander.com/java/java-uimanager-color-keys-list/ +// TODO: Make the code less spaghetti later +public class BaseJFrame extends JFrame { + public BaseJFrame() { + init(); + } + public BaseJFrame(boolean show, boolean resizable) { + init(); + setVisible(show); + setResizable(resizable); + } + + public void init() { + setTitle(SingletonInjector.INSTANCE.get(ILangWrapper.class).getLang("lod.title")); + try { + setIconImage(new FlatSVGIcon(JarUtils.accessFile("iconLegacy.svg")).getImage()); // SVG Salamander (the library which we use for svg files) doesn't support css class colors + } catch (Exception e) {e.printStackTrace();} + setSize(720, 480); + setLocationRelativeTo(null); // Puts the window at the middle of the screen + setLayout(new BorderLayout()); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + initLookAndFeel(); + } + + /** + * Buttons for language and theme changing + * + * @param themeOnBottom Puts the theme buttons below the language + * @param rootPosOnLeft Where the start for the x is (on the left of the buttons or on the right) + */ + public void addExtraButtons(int x, int y, boolean themeOnBottom, boolean rootPosOnLeft) { + // ========== LANGUAGE ========== + int langBoxHeight = 25; + int langBoxWidth = 100; + + // Creates a list with all the options in it + List langsToChoose = new ArrayList<>(); + try( + final InputStreamReader isr = new InputStreamReader(JarUtils.accessFile("assets/lod/lang"), StandardCharsets.UTF_8); + final BufferedReader br = new BufferedReader(isr) + ) { + List col = Collections.unmodifiableList(new ArrayList<>(Arrays.asList(br.lines().toArray()))); + for (Object obj: col) + langsToChoose.add(((String) obj).replaceAll("\\.json", "")); + } catch (Exception e) {e.printStackTrace();} + + // Creates the box + JComboBox languageBox = new JComboBox(new DefaultComboBoxModel(langsToChoose.toArray())); + languageBox.setSelectedIndex(langsToChoose.indexOf(Locale.getDefault().toString().toLowerCase())); + languageBox.addActionListener( e -> { + Locale.setDefault(Locale.forLanguageTag(languageBox.getSelectedItem().toString())); // Change lang on update + } ); + // Set where it goes + languageBox.setBounds(rootPosOnLeft? x : x-langBoxWidth, themeOnBottom? y : y+langBoxHeight, langBoxWidth, langBoxHeight); + // And finally add it + add(languageBox); + + + // ========== THEMING ========== + // TODO: Change the theme to a toggle switch rather than having 2 buttons + int themeButtonSize = 25; + JButton lightMode = null; + JButton darkMode = null; + // Try to set the icons for them + try { + lightMode = new JButton(new ImageIcon( + new FlatSVGIcon(JarUtils.accessFile("assets/lod/textures/jar/themeLight.svg")).getImage() // Get the image + .getScaledInstance(themeButtonSize, themeButtonSize, Image.SCALE_DEFAULT) // Scale it to the correct size + )); + darkMode = new JButton(new ImageIcon( + new FlatSVGIcon(JarUtils.accessFile("assets/lod/textures/jar/themeDark.svg")).getImage() // Get the image + .getScaledInstance(themeButtonSize, themeButtonSize, Image.SCALE_DEFAULT) // Scale it to the correct size + )); + } catch (Exception e) {e.printStackTrace();} + // Where do the buttons go + lightMode.setBounds(rootPosOnLeft? x : x-(themeButtonSize*2), themeOnBottom? y+langBoxHeight: y, themeButtonSize, themeButtonSize); + darkMode.setBounds(rootPosOnLeft? x+themeButtonSize : x-themeButtonSize, themeOnBottom? y+langBoxHeight: y, themeButtonSize, themeButtonSize); + // Tell buttons what to do + lightMode.addActionListener(e -> { + FlatLightLaf.setup(); + FlatLightLaf.updateUI(); + }); + darkMode.addActionListener(e -> { + FlatDarkLaf.setup(); + FlatDarkLaf.updateUI(); + }); + // Finally add the buttons + add(lightMode); + add(darkMode); + } + + public BaseJFrame addLogo() { + int logoHeight = 200; + + JPanel logo = new JPanel() { + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + try { + BufferedImage image = ImageIO.read(JarUtils.accessFile("logo.png")); + int logoWidth = (int) ((double) logoHeight * ((double) image.getWidth() / (double) image.getHeight())); // Calculate the aspect ratio and set the height correctly to not stretch it + g.drawImage(image, (getWidth()/2)-(logoWidth/2), 0, logoWidth, logoHeight,this); // Resize image and draw it + } catch (Exception e) {e.printStackTrace();} + } + }; + logo.setBounds(logo.getX(), logo.getY(), logo.getWidth(), logo.getHeight()); + + add(logo); + + return this; + } + + + + + // This part of the code is taken from the official java docs at https://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html + + // Specify the look and feel to use by defining the LOOKANDFEEL constant + // Valid values are: null (use the default), "Metal", "System", "Motif", + // and "GTK" + final static String LOOKANDFEEL = "GTK"; + private static void initLookAndFeel() { + String lookAndFeel = null; + + if (LOOKANDFEEL != null) { + if (LOOKANDFEEL.equals("Metal")) { + lookAndFeel = UIManager.getCrossPlatformLookAndFeelClassName(); + // an alternative way to set the Metal L&F is to replace the + // previous line with: + // lookAndFeel = "javax.swing.plaf.metal.MetalLookAndFeel"; + + } else if (LOOKANDFEEL.equals("System")) { + lookAndFeel = UIManager.getSystemLookAndFeelClassName(); + } else if (LOOKANDFEEL.equals("Motif")) { + lookAndFeel = "com.sun.java.swing.plaf.motif.MotifLookAndFeel"; + } else if (LOOKANDFEEL.equals("GTK")) { + lookAndFeel = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel"; + } else { + System.err.println("Unexpected value of LOOKANDFEEL specified: " + + LOOKANDFEEL); + lookAndFeel = UIManager.getCrossPlatformLookAndFeelClassName(); + } + + try { + UIManager.setLookAndFeel(lookAndFeel); + } catch (ClassNotFoundException e) { + System.err.println("Couldn't find class for specified look and feel:" + + lookAndFeel); + System.err.println("Did you include the L&F library in the class path?"); + System.err.println("Using the default look and feel."); + } catch (UnsupportedLookAndFeelException e) { + System.err.println("Can't use the specified look and feel (" + + lookAndFeel + + ") on this platform."); + System.err.println("Using the default look and feel."); + } catch (Exception e) { + System.err.println("Couldn't get specified look and feel (" + + lookAndFeel + + "), for some reason."); + System.err.println("Using the default look and feel."); + e.printStackTrace(); + } + } + } +} + diff --git a/core/src/main/java/com/seibel/lod/core/jar/wrapperInterfaces/config/LangWrapper.java b/core/src/main/java/com/seibel/lod/core/jar/wrapperInterfaces/config/LangWrapper.java index b95450735..eb59306fc 100644 --- a/core/src/main/java/com/seibel/lod/core/jar/wrapperInterfaces/config/LangWrapper.java +++ b/core/src/main/java/com/seibel/lod/core/jar/wrapperInterfaces/config/LangWrapper.java @@ -13,7 +13,7 @@ import java.util.Locale; public class LangWrapper implements ILangWrapper { public static final LangWrapper INSTANCE = new LangWrapper(); private static final Config jsonObject = Config.inMemory(); - private static final Logger logger = LogManager.getLogger(LangWrapper.class); + private static final Logger logger = LogManager.getLogger(LangWrapper.class.getSimpleName()); public static void init() { try { diff --git a/core/src/main/resources/iconLegacy.svg b/core/src/main/resources/iconLegacy.svg new file mode 100644 index 000000000..05550aff2 --- /dev/null +++ b/core/src/main/resources/iconLegacy.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +