From 13c7d86f0ac6636282b0181c320a80a62458c9a5 Mon Sep 17 00:00:00 2001 From: LeeWyatt Date: Sun, 22 Oct 2023 20:23:11 +0900 Subject: [PATCH] Add Px2EmConverter; Hide fields in ShowcaseOverview box that are not used --- .../components/DoubleTextField.java | 6 +- .../overviewbox/ShowcaseOverviewBox.java | 84 ++++--- .../overviewbox/UtilityOverviewBox.java | 11 +- .../pxemconverter/Px2EmConverter.java | 125 ++++++++++ .../utilities/pxemconverter/Px2EmRules.java | 10 + .../utilities/pxemconverter/Px2EmView.java | 213 ++++++++++++++++++ .../com/dlsc/jfxcentral2/utils/FileUtil.java | 15 ++ .../com/dlsc/jfxcentral2/utils/IkonUtil.java | 2 + .../resources/com/dlsc/jfxcentral2/theme.css | 124 ++++++++++ .../pxemconverter/css-px2em-rules.json | 21 ++ 10 files changed, 577 insertions(+), 34 deletions(-) create mode 100644 components/src/main/java/com/dlsc/jfxcentral2/utilities/pxemconverter/Px2EmConverter.java create mode 100644 components/src/main/java/com/dlsc/jfxcentral2/utilities/pxemconverter/Px2EmRules.java create mode 100644 components/src/main/java/com/dlsc/jfxcentral2/utilities/pxemconverter/Px2EmView.java create mode 100644 components/src/main/resources/com/dlsc/jfxcentral2/utilities/pxemconverter/css-px2em-rules.json diff --git a/components/src/main/java/com/dlsc/jfxcentral2/components/DoubleTextField.java b/components/src/main/java/com/dlsc/jfxcentral2/components/DoubleTextField.java index ec5d148a..49a9d652 100644 --- a/components/src/main/java/com/dlsc/jfxcentral2/components/DoubleTextField.java +++ b/components/src/main/java/com/dlsc/jfxcentral2/components/DoubleTextField.java @@ -29,7 +29,11 @@ public DoubleTextField() { if (text == null || text.isEmpty() || SPECIAL_CASES.contains(text)) { return 0.0; } - return Double.parseDouble(text); + try { + return Double.parseDouble(text); + } catch (NumberFormatException e) { + return 0.0; + } }, textProperty())); } diff --git a/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/ShowcaseOverviewBox.java b/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/ShowcaseOverviewBox.java index 55a5db7a..c8d66ed2 100644 --- a/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/ShowcaseOverviewBox.java +++ b/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/ShowcaseOverviewBox.java @@ -11,6 +11,7 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.RowConstraints; import javafx.scene.layout.VBox; +import org.apache.commons.lang3.StringUtils; import java.time.format.DateTimeFormatter; @@ -31,32 +32,17 @@ public ShowcaseOverviewBox(RealWorldApp app) { @Override protected Node createTopNode() { - Label locationHeader = new Label("LOCATION"); - locationHeader.getStyleClass().add("field-title"); + FieldGroup locationGroup = new FieldGroup("LOCATION", new String[]{"field-value"}); + locationLabel = locationGroup.getLabel(); - locationLabel = new Label(); - locationLabel.getStyleClass().add("field-value"); - locationLabel.setWrapText(true); + FieldGroup domainGroup = new FieldGroup("DOMAIN", new String[]{"field-value"}); + domainLabel = domainGroup.getLabel(); - Label domainHeader = new Label("DOMAIN"); - domainHeader.getStyleClass().add("field-title"); + FieldGroup createdByGroup = new FieldGroup("CREATED BY", new String[]{"field-value", "created-by-label"}); + createdByLabel = createdByGroup.getLabel(); - domainLabel = new Label(); - domainLabel.getStyleClass().add("field-value"); - domainLabel.setWrapText(true); - - Label createdByHeader = new Label("CREATED BY"); - createdByHeader.getStyleClass().add("field-title"); - - createdByLabel = new Label(); - createdByLabel.getStyleClass().addAll("field-value", "created-by-label"); - createdByLabel.setWrapText(true); - - Label createdOnHeader = new Label("CREATED ON"); - createdOnHeader.getStyleClass().add("field-title"); - - createdOnLabel = new Label(); - createdOnLabel.getStyleClass().add("field-value"); + FieldGroup createdOnGroup = new FieldGroup("CREATED ON", new String[]{"field-value"}); + createdOnLabel = createdOnGroup.getLabel(); setBaseURL(DataRepository2.getInstance().getRepositoryDirectoryURL() + "realworld/" + getModel().getId()); fillData(); @@ -67,7 +53,15 @@ protected Node createTopNode() { for (int i = 0; i < 4; i++) { ColumnConstraints columnConstraints = new ColumnConstraints(); columnConstraints.setHalignment(HPos.LEFT); - columnConstraints.setHgrow(Priority.ALWAYS); + if (i == 0) { + columnConstraints.hgrowProperty().bind(locationLabel.visibleProperty().map(b -> b ? Priority.ALWAYS : Priority.NEVER)); + }else if (i == 1) { + columnConstraints.hgrowProperty().bind(domainLabel.visibleProperty().map(b -> b ? Priority.ALWAYS : Priority.NEVER)); + }else if (i == 2) { + columnConstraints.hgrowProperty().bind(createdByLabel.visibleProperty().map(b -> b ? Priority.ALWAYS : Priority.NEVER)); + } else { + columnConstraints.hgrowProperty().bind(createdOnLabel.visibleProperty().map(b -> b ? Priority.ALWAYS : Priority.NEVER)); + } gridPane.getColumnConstraints().add(columnConstraints); } for (int i = 0; i < 2; i++) { @@ -75,24 +69,24 @@ protected Node createTopNode() { rowConstraints.setValignment(VPos.TOP); gridPane.getRowConstraints().add(rowConstraints); } - gridPane.add(locationHeader, 0, 0); + gridPane.add(locationGroup.getHeader(), 0, 0); gridPane.add(locationLabel, 0, 1); - gridPane.add(domainHeader, 1, 0); + gridPane.add(domainGroup.getHeader(), 1, 0); gridPane.add(domainLabel, 1, 1); - gridPane.add(createdByHeader, 2, 0); + gridPane.add(createdByGroup.getHeader(), 2, 0); gridPane.add(createdByLabel, 2, 1); - gridPane.add(createdOnHeader, 3, 0); + gridPane.add(createdOnGroup.getHeader(), 3, 0); gridPane.add(createdOnLabel, 3, 1); return gridPane; } else { VBox topBox = new VBox( - locationHeader, + locationGroup.getHeader(), locationLabel, - domainHeader, + domainGroup.getHeader(), domainLabel, - createdByHeader, + createdByGroup.getHeader(), createdByLabel, - createdOnHeader, + createdOnGroup.getHeader(), createdOnLabel ); createdOnLabel.getStyleClass().add("last"); @@ -119,4 +113,30 @@ private void fillData() { setMarkdown(""); } } + + private static class FieldGroup { + private final Label header; + private final Label label; + + public FieldGroup(String headerText, String[] labelStyles) { + header = new Label(headerText); + header.getStyleClass().add("field-title"); + header.managedProperty().bind(header.visibleProperty()); + + label = new Label(); + label.getStyleClass().addAll(labelStyles); + label.setWrapText(true); + label.managedProperty().bind(label.visibleProperty()); + label.visibleProperty().bind(label.textProperty().map(s -> !StringUtils.isEmpty(s) && !StringUtils.equalsIgnoreCase(s, "Unknown"))); + header.visibleProperty().bind(label.visibleProperty()); + } + + public Label getHeader() { + return header; + } + + public Label getLabel() { + return label; + } + } } diff --git a/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/UtilityOverviewBox.java b/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/UtilityOverviewBox.java index ee856526..bfceaf63 100644 --- a/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/UtilityOverviewBox.java +++ b/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/UtilityOverviewBox.java @@ -4,14 +4,16 @@ import com.dlsc.jfxcentral.data.model.Utility; import com.dlsc.jfxcentral2.components.CustomMarkdownView; import com.dlsc.jfxcentral2.utilities.cssplayground.CssPlaygroundView; +import com.dlsc.jfxcentral2.utilities.effectdesigner.EffectDesignerView; import com.dlsc.jfxcentral2.utilities.paintpicker.GradientDesignerView; import com.dlsc.jfxcentral2.utilities.pathextractor.SVGPathExtractorView; -import com.dlsc.jfxcentral2.utilities.effectdesigner.EffectDesignerView; +import com.dlsc.jfxcentral2.utilities.pxemconverter.Px2EmView; import com.dlsc.jfxcentral2.utils.ModelObjectTool; import javafx.scene.Node; import javafx.scene.control.Label; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; +import org.kordamp.ikonli.carbonicons.CarbonIcons; import org.kordamp.ikonli.elusive.Elusive; import org.kordamp.ikonli.fileicons.FileIcons; import org.kordamp.ikonli.javafx.FontIcon; @@ -72,6 +74,13 @@ private Node createToolPane(Utility model) { EffectDesignerView effectDesignerView = new EffectDesignerView(); effectDesignerView.sizeProperty().bind(sizeProperty()); return effectDesignerView; + } else if (StringUtils.equalsIgnoreCase(model.getId(), "pxtoemconverter")) { + setTitle("Pixel-to-EM Converter"); + setIcon(CarbonIcons.CD_CREATE_EXCHANGE); + getStyleClass().add("px-2-em-overview-box"); + Px2EmView view = new Px2EmView(); + view.sizeProperty().bind(sizeProperty()); + return view; } return createComingSoonPane(); diff --git a/components/src/main/java/com/dlsc/jfxcentral2/utilities/pxemconverter/Px2EmConverter.java b/components/src/main/java/com/dlsc/jfxcentral2/utilities/pxemconverter/Px2EmConverter.java new file mode 100644 index 00000000..136e3477 --- /dev/null +++ b/components/src/main/java/com/dlsc/jfxcentral2/utilities/pxemconverter/Px2EmConverter.java @@ -0,0 +1,125 @@ +package com.dlsc.jfxcentral2.utilities.pxemconverter; + +import com.dlsc.jfxcentral2.utils.NumberUtil; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Px2EmConverter { + private Px2EmConverter() { + } + + /** + * Converts px units to em units for specified CSS properties in a CSS file. + * + * @param sourceFile the source CSS file + * @param targetFile the target CSS file + * @param propertiesToConvert a list of CSS properties to be converted + * @param baseFontSize the base font size for em unit calculation + * @param addComment whether to add original value as comment + * @throws IOException if an I/O error occurs + */ + public static void convert(File sourceFile, File targetFile, List propertiesToConvert, double baseFontSize, boolean addComment) throws IOException { + String content = Files.readString(sourceFile.toPath()); + Pattern commentPattern = Pattern.compile("/\\*.*?\\*/"); + Matcher commentMatcher = commentPattern.matcher(content); + + Map commentsMap = new HashMap<>(); + while (commentMatcher.find()) { + commentsMap.put(commentMatcher.start(), commentMatcher.group()); + } + + for (String property : propertiesToConvert) { + String regex = property + "\\s*:\\s*([0-9]+(?:\\.[0-9]+)?(?:em|px)?(?:\\s*[0-9]+(?:\\.[0-9]+)?(?:em|px)?)*)\\s*;?"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(content); + + StringBuilder sb = new StringBuilder(); + while (matcher.find()) { + int start = matcher.start(); + int end = matcher.end(); + boolean inComment = false; + for (Map.Entry entry : commentsMap.entrySet()) { + int commentStart = entry.getKey(); + int commentEnd = commentStart + entry.getValue().length(); + if (start >= commentStart && end <= commentEnd) { + inComment = true; + break; + } + } + + if (!inComment) { + String values = matcher.group(1); + if (!values.contains("em")) { + String emValues = convertToEm(values, baseFontSize); + if (addComment) { + matcher.appendReplacement(sb, property + ": " + emValues + ";" + "/* " + values + " */"); + } else { + matcher.appendReplacement(sb, property + ": " + emValues + ";"); + } + } else { + matcher.appendReplacement(sb, property + ": " + values + ";"); + } + } + } + matcher.appendTail(sb); + content = sb.toString(); + } + + Files.writeString(Objects.requireNonNullElse(targetFile, sourceFile).toPath(), content); + } + + //public static void convert(File sourceFile, File targetFile, List propertiesToConvert, double baseFontSize, boolean addComment) throws IOException { + // String content = Files.readString(sourceFile.toPath()); + // + // for (String property : propertiesToConvert) { + // String regex = property + "\\s*:\\s*([0-9]+(?:\\.[0-9]+)?(?:em|px)?(?:\\s*[0-9]+(?:\\.[0-9]+)?(?:em|px)?)*)\\s*;?"; + // Pattern pattern = Pattern.compile(regex); + // Matcher matcher = pattern.matcher(content); + // + // StringBuilder sb = new StringBuilder(); + // while (matcher.find()) { + // String values = matcher.group(1); + // if (!values.contains("em")) { + // String emValues = convertToEm(values, baseFontSize); + // if (addComment) { + // matcher.appendReplacement(sb, property + ": " + emValues + ";" + " /* " + values + " */"); + // } else { + // matcher.appendReplacement(sb, property + ": " + emValues + ";"); + // } + // } else { + // matcher.appendReplacement(sb, property + ": " + values + ";"); + // } + // } + // matcher.appendTail(sb); + // content = sb.toString(); + // } + // + // Files.writeString(Objects.requireNonNullElse(targetFile, sourceFile).toPath(), content); + //} + + public static String convertToEm(String values, double baseFontSize) { + if (values.trim().endsWith("em")) { + return values.trim(); + } + + String[] valueArray = values.split("\\s+"); + StringBuilder emBuilder = new StringBuilder(); + for (String value : valueArray) { + if (!value.isEmpty()) { + double px = Double.parseDouble(value.replaceAll("px", "")); + double em = px / baseFontSize; + emBuilder.append(NumberUtil.trimTrailingZeros(em, 5)).append("em").append(" "); + } + } + return emBuilder.toString().trim(); + } + +} diff --git a/components/src/main/java/com/dlsc/jfxcentral2/utilities/pxemconverter/Px2EmRules.java b/components/src/main/java/com/dlsc/jfxcentral2/utilities/pxemconverter/Px2EmRules.java new file mode 100644 index 00000000..60ed54e6 --- /dev/null +++ b/components/src/main/java/com/dlsc/jfxcentral2/utilities/pxemconverter/Px2EmRules.java @@ -0,0 +1,10 @@ +package com.dlsc.jfxcentral2.utilities.pxemconverter; + +import java.util.List; + +/** + * convertibleProps: properties that can be converted from px to em; + * ignoredProps: properties that should be ignored when converting from px to em. + */ +public record Px2EmRules(List convertibleProps, List ignoredProps) { +} diff --git a/components/src/main/java/com/dlsc/jfxcentral2/utilities/pxemconverter/Px2EmView.java b/components/src/main/java/com/dlsc/jfxcentral2/utilities/pxemconverter/Px2EmView.java new file mode 100644 index 00000000..343cb0a4 --- /dev/null +++ b/components/src/main/java/com/dlsc/jfxcentral2/utilities/pxemconverter/Px2EmView.java @@ -0,0 +1,213 @@ +package com.dlsc.jfxcentral2.utilities.pxemconverter; + +import com.dlsc.jfxcentral2.components.DoubleTextField; +import com.dlsc.jfxcentral2.components.FileHandlerView; +import com.dlsc.jfxcentral2.components.PaneBase; +import com.dlsc.jfxcentral2.utils.FileUtil; +import com.dlsc.jfxcentral2.utils.IkonUtil; +import com.dlsc.jfxcentral2.utils.NumberUtil; +import com.dlsc.jfxcentral2.utils.WebAPIUtil; +import com.google.gson.Gson; +import com.jpro.webapi.WebAPI; +import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; +import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.stage.FileChooser; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.controlsfx.control.ListSelectionView; +import org.kordamp.ikonli.bootstrapicons.BootstrapIcons; +import org.kordamp.ikonli.carbonicons.CarbonIcons; +import org.kordamp.ikonli.hawcons.HawconsStroke; +import org.kordamp.ikonli.javafx.FontIcon; +import org.kordamp.ikonli.material.Material; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.util.Collections; +import java.util.Objects; + +public class Px2EmView extends PaneBase { + private static final Logger LOGGER = LogManager.getLogger(Px2EmView.class); + private static final String CACHE_PATH = ".jfxcentral/csscache"; + private static final String TEMP_DIR_PREFIX = "tempDir_"; + private static final String TEMP_DIR_SUFFIX = "_css"; + private static final double MAX_FONT_SIZE = 200; + private static final double MIN_FONT_SIZE = 5; + private static final double DEFAULT_FONT_SIZE = 12; + private File initDirectory; + private File cssFile; + + public Px2EmView() { + getStyleClass().add("px-2-em-view"); + + Label label = new Label("Base Font Size", new FontIcon(IkonUtil.font)); + + DoubleTextField fontSizeField = new DoubleTextField(); + fontSizeField.setText(String.valueOf(DEFAULT_FONT_SIZE)); + fontSizeField.setPromptText("Input Default Font Size (px)"); + fontSizeField.focusedProperty().addListener((observable, oldValue, newValue) -> { + if (!newValue) { + double value = NumberUtil.clamp(fontSizeField.getValue(), MIN_FONT_SIZE, MAX_FONT_SIZE); + fontSizeField.setText(NumberUtil.trimTrailingZeros(value, 2)); + } + }); + + Label convertTipsLabel = new Label("px = 1em"); + convertTipsLabel.getStyleClass().add("convert-tips-label"); + + Label fontSizeTips = new Label("Please Enter base px for em conversion."); + fontSizeTips.getStyleClass().add("size-tips-label"); + fontSizeTips.setGraphic(new FontIcon(IkonUtil.tip)); + + FlowPane fontSizePane = new FlowPane(label, fontSizeField, convertTipsLabel, fontSizeTips); + fontSizePane.getStyleClass().add("font-size-pane"); + + ListSelectionView view = new ListSelectionView<>(); + view.getSourceItems().addAll(loadRules().convertibleProps()); + Label convertibleLabel = new Label("Convertible Props", new FontIcon(BootstrapIcons.CHECK_CIRCLE)); + view.setSourceHeader(convertibleLabel); + + Label ignoredProps = new Label("Ignored Props", new FontIcon(Material.DO_NOT_DISTURB)); + view.setTargetHeader(ignoredProps); + view.getTargetItems().addAll(loadRules().ignoredProps()); + + FileHandlerView cssFileHandlerView = new FileHandlerView(); + cssFileHandlerView.setText("Click or drop CSS file here."); + cssFileHandlerView.getSupportedExtensions().add(".css"); + + Label fileNameLabel = new Label("File Name:", new FontIcon(HawconsStroke.DOCUMENT_FILE_CSS)); + fileNameLabel.getStyleClass().add("name-label"); + + Label fileNameValue = new Label(); + fileNameValue.getStyleClass().add("name-value-label"); + cssFileHandlerView.setOnUploadedFile(file -> { + fileNameValue.setText(file.getName()); + cssFile = file; + }); + + HBox fileNameBox = new HBox(fileNameLabel, fileNameValue); + fileNameBox.getStyleClass().add("file-name-box"); + fileNameBox.managedProperty().bind(fileNameBox.visibleProperty()); + fileNameBox.visibleProperty().bind(fileNameValue.textProperty().isNotEmpty()); + + CheckBox checkBox = new CheckBox("Preserve original values as comments."); + checkBox.setSelected(true); + + Label egLabel = new Label("e.g. -fx-font-size: 2em; /* 24px */"); + egLabel.getStyleClass().add("eg-label"); + + FlowPane flowPane = new FlowPane(checkBox, egLabel); + flowPane.getStyleClass().add("flow-pane"); + + Button convertButton = new Button("Convert", new FontIcon(CarbonIcons.CD_CREATE_EXCHANGE)); + convertButton.getStyleClass().addAll("fill-button", "convert-button"); + convertButton.disableProperty().bind(fileNameBox.visibleProperty().not()); + convertButton.setMaxWidth(Double.MAX_VALUE); + + convertButton.setOnAction(event -> { + double fontSize = NumberUtil.clamp(fontSizeField.getValue(), MIN_FONT_SIZE, MAX_FONT_SIZE); + if (WebAPI.isBrowser()) { + webDownloadFile(cssFile, fontSize); + } else { + desktopDownloadFile(cssFile, fontSize); + } + }); + + VBox container = new VBox(fontSizePane, view, cssFileHandlerView, fileNameBox, flowPane, convertButton); + container.getStyleClass().add("container"); + getChildren().setAll(container); + } + + private void desktopDownloadFile(File cssFile, double baseFontSize) { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Save File"); + fileChooser.setInitialFileName(FileUtil.getFileNameWithoutExtension(cssFile) + "_converted.css"); + if (initDirectory != null) { + fileChooser.setInitialDirectory(initDirectory); + } else { + fileChooser.setInitialDirectory(cssFile.getParentFile()); + } + fileChooser.getExtensionFilters().addAll( + new FileChooser.ExtensionFilter("CSS Files", "*.css"), + new FileChooser.ExtensionFilter("All Files", "*.*") + ); + + File targetFile = fileChooser.showSaveDialog(getScene().getWindow()); + if (targetFile != null) { + initDirectory = targetFile.getParentFile(); + try { + Px2EmConverter.convert(cssFile, targetFile, loadRules().convertibleProps(), baseFontSize, true); + } catch (IOException e) { + LOGGER.error("Failed to convert px to em for file {}", cssFile.getAbsolutePath(), e); + } + } + } + + private void webDownloadFile(File cssFile, double baseFontSize) { + File cacheDir = getCacheDirectory(); + File tempFile = createCssTempFile(cacheDir, cssFile, baseFontSize); + + try { + WebAPIUtil.getWebAPI(this).downloadURL(tempFile.toURI().toURL(), () -> FileUtil.clearDirectory(cacheDir)); + } catch (MalformedURLException e) { + LOGGER.error("Failed to convert css file to URL: {}", tempFile, e); + throw new RuntimeException(e); + } + } + + private File getCacheDirectory() { + File cacheDir = new File(System.getProperty("user.home"), CACHE_PATH); + if (!cacheDir.exists() && !cacheDir.mkdir()) { + LOGGER.error("Failed to create cache directory: {}", cacheDir); + throw new RuntimeException("Failed to create css cache directory: " + cacheDir); + } + return cacheDir; + } + + private File createCssTempFile(File cacheDir, File cssFile, double baseFontSize) { + File tempDir = createTempDirectory(cacheDir); + File tempFile = new File(tempDir, FileUtil.getFileNameWithoutExtension(cssFile) + "_converted.css"); + try { + Px2EmConverter.convert(cssFile, tempFile, loadRules().convertibleProps(), baseFontSize, true); + } catch (IOException e) { + LOGGER.error("Failed to convert px to em for file {}", cssFile.getAbsolutePath(), e); + } + return tempFile; + } + + private File createTempDirectory(File parentDir) { + File tempDir = null; + try { + tempDir = File.createTempFile(TEMP_DIR_PREFIX, TEMP_DIR_SUFFIX, parentDir); + if (!tempDir.delete() || !tempDir.mkdir()) { + LOGGER.error("Failed to create temp directory: {}", tempDir); + throw new IOException("Failed to create a temporary directory for css."); + } + } catch (IOException e) { + LOGGER.error("Failed to create css temp directory: {}", tempDir, e); + throw new RuntimeException(e); + } + return tempDir; + } + + private Px2EmRules loadRules() { + String filePath = "/com/dlsc/jfxcentral2/utilities/pxemconverter/css-px2em-rules.json"; + try (InputStream inputStream = getClass().getResourceAsStream(filePath); + InputStreamReader reader = new InputStreamReader(Objects.requireNonNull(inputStream)); + BufferedReader bufferedReader = new BufferedReader(reader)) { + return new Gson().fromJson(bufferedReader, Px2EmRules.class); + } catch (IOException e) { + LOGGER.error("Failed to load px2em rules from {}", filePath, e); + return new Px2EmRules(Collections.emptyList(), Collections.emptyList()); + } + } + +} diff --git a/components/src/main/java/com/dlsc/jfxcentral2/utils/FileUtil.java b/components/src/main/java/com/dlsc/jfxcentral2/utils/FileUtil.java index 127d2547..8192315e 100644 --- a/components/src/main/java/com/dlsc/jfxcentral2/utils/FileUtil.java +++ b/components/src/main/java/com/dlsc/jfxcentral2/utils/FileUtil.java @@ -49,4 +49,19 @@ public static String extractFileExtension(String fileName) { return null; } + /** + * Returns the name of the file without its file extension. + * + * @param file the file + * @return the name of the file without its file extension + */ + public static String getFileNameWithoutExtension(File file) { + String fileName = file.getName(); + int dotIndex = fileName.lastIndexOf("."); + if (dotIndex == -1) { + return fileName; + } + return fileName.substring(0, dotIndex); + } + } \ No newline at end of file diff --git a/components/src/main/java/com/dlsc/jfxcentral2/utils/IkonUtil.java b/components/src/main/java/com/dlsc/jfxcentral2/utils/IkonUtil.java index ec51752c..1a586ced 100644 --- a/components/src/main/java/com/dlsc/jfxcentral2/utils/IkonUtil.java +++ b/components/src/main/java/com/dlsc/jfxcentral2/utils/IkonUtil.java @@ -34,6 +34,7 @@ import org.kordamp.ikonli.ionicons4.Ionicons4IOS; import org.kordamp.ikonli.lineawesome.LineAwesomeSolid; import org.kordamp.ikonli.material.Material; +import org.kordamp.ikonli.material2.Material2AL; import org.kordamp.ikonli.material2.Material2MZ; import org.kordamp.ikonli.materialdesign.MaterialDesign; import org.kordamp.ikonli.materialdesign2.MaterialDesignA; @@ -124,6 +125,7 @@ public interface IkonUtil { Ikon ury = Subway.RECTANGLE_2; Ikon fraction = CarbonIcons.REFLECT_VERTICAL; Ikon topOffset = AntDesignIconsOutlined.VERTICAL_ALIGN_TOP; + Ikon font = Material2AL.FORMAT_SIZE; static Ikon getModelIkon(ModelObject mo) { return getModelIkon(mo.getClass()); diff --git a/components/src/main/resources/com/dlsc/jfxcentral2/theme.css b/components/src/main/resources/com/dlsc/jfxcentral2/theme.css index 6e6acce7..1ae2b4c6 100644 --- a/components/src/main/resources/com/dlsc/jfxcentral2/theme.css +++ b/components/src/main/resources/com/dlsc/jfxcentral2/theme.css @@ -110,6 +110,8 @@ -mdfx-bq-color-border: #4488cc; /* the blockquote's border color */ -mdfx-bq-color-background: #0000ff0c; /* the blockquote's background color */ + + -fx-focus-traversable: false; } .root .normal-page { @@ -271,6 +273,8 @@ } .normal-page .text-field { + -fx-focus-color: -grey-30; + -fx-faint-focus-color: transparent; -fx-focus-color: -grey-30; -fx-faint-focus-color: transparent; -fx-background-radius: 0px; @@ -9747,3 +9751,123 @@ -fx-background-color: transparent; -fx-border-width: 0; } + +/** ---------------------------------------------------------------------------- + * Px2EmView + */ +.px-2-em-view > .container { + -fx-spacing: 10px; +} + +.px-2-em-view > .container .font-size-pane { + -fx-alignment: bottom-left; + -fx-hgap: 5px; + -fx-vgap: 5px; +} + +.px-2-em-view > .container .font-size-pane .text-field { + -fx-max-width: 50px; +} + +.px-2-em-view > .container .font-size-pane .convert-tips-label { + -fx-padding: 0 30px 0 0; +} + +.px-2-em-view > .container .size-tips-label { + -fx-text-fill: -grey-60; + -fx-font-family: "Roboto Condensed"; + -fx-font-size: 17px; +} + +.px-2-em-view > .container .size-tips-label .ikonli-font-icon { + -fx-icon-color: -grey-60; +} + +.px-2-em-view > .container .file-handler-view { + -fx-background-color: #f7f7f7; + -fx-padding: 5px 0; + -fx-border-color: -grey-60; + -fx-border-style: dashed; + -fx-border-width: 1px; +} + +.px-2-em-view > .container .file-handler-view.active { + -fx-background-color: -grey-10; +} + +.px-2-em-view > .container .list-selection-view { + -fx-padding: 5px 0; +} + +.px-2-em-view > .container .list-selection-view > .grid-pane { + -fx-vgap: 5px; + -fx-hgap: 5px; +} + +.px-2-em-view > .container .list-selection-view > .grid-pane .list-view { + -fx-focus-color: -grey-30; + -fx-faint-focus-color: transparent; +} + +.px-2-em-view .list-selection-view > .grid-pane .list-view .list-cell:selected { + -fx-background-color: -light-blue; + -fx-text-fill: -grey-100; +} + +.px-2-em-view .list-selection-view > .grid-pane .button { + -fx-background-radius: 0px; + -fx-background-color: -grey-10; + -fx-border-color: -grey-30; +} + +.px-2-em-view .list-selection-view > .grid-pane .button:hover { + -fx-background-color: derive(-grey-10, 30%) +} + +.px-2-em-view .list-selection-view > .grid-pane .button:pressed { + -fx-background-color: derive(-grey-10, -20%) +} + +.px-2-em-view > .container .file-name-box { + -fx-alignment: center-left; + -fx-spacing: 5px; +} + +.px-2-em-view > .container .name-label { + -fx-pref-width: 115px; + -fx-min-width: 115px; +} + +.px-2-em-view > .container .name-label .ikonli-font-icon { + -fx-icon-size: 25px; +} + +.px-2-em-view > .container .name-value-label { + -fx-text-overrun: leading-ellipsis; + -fx-text-fill: -grey-60; +} + +.px-2-em-view > .container .check-box { + -fx-icon-size: 25px; + -fx-border-color: transparent; +} + +.px-2-em-view > .container .check-box .box { + -fx-border-color: -grey-10; +} + +.px-2-em-view > .container .convert-button { + -fx-font-size: 18px; + -fx-graphic-text-gap: 10px; +} + +.px-2-em-view > .container .convert-button .ikonli-font-icon { + -fx-icon-size: 25px; + -fx-translate-y: 2px; +} + +.px-2-em-view > .container .eg-label { + -fx-text-fill: -grey-30; + -fx-font-family: "JetBrains Mono Medium"; + -fx-padding: 0 0 0 35px; +} \ No newline at end of file diff --git a/components/src/main/resources/com/dlsc/jfxcentral2/utilities/pxemconverter/css-px2em-rules.json b/components/src/main/resources/com/dlsc/jfxcentral2/utilities/pxemconverter/css-px2em-rules.json new file mode 100644 index 00000000..2af3e428 --- /dev/null +++ b/components/src/main/resources/com/dlsc/jfxcentral2/utilities/pxemconverter/css-px2em-rules.json @@ -0,0 +1,21 @@ +{ + "convertibleProps": [ + "-fx-font-size", + "-fx-padding", + "-fx-pref-width", + "-fx-pref-height", + "-fx-min-width", + "-fx-min-height", + "-fx-max-width", + "-fx-max-height", + "-fx-spacing", + "-fx-background-radius", + "-fx-border-radius" + ], + "ignoredProps": [ + "-fx-border-width", + "-fx-stroke-width", + "-fx-line-spacing", + "-fx-insets" + ] +}