From e1fa91563afe8ea44a715508249b1027e83f2bcb Mon Sep 17 00:00:00 2001 From: ArghKevin Date: Sat, 25 May 2024 19:00:13 -0700 Subject: Implmented interactive buttons. --- src/CSVReader.java | 2 +- src/ComparisonView.java | 42 ++++++++++++++++++++++++----- src/FamilyButton.java | 31 ++++++++++++++++++++++ src/FamilyButtonListener.java | 61 ++++++++++++++++++++++++++++++++++++++++++ src/FontFamily.java | 62 ++++++++++++++++++++++++++++++++++++++++++- src/JSONReader.java | 15 ++++++++--- src/LineGraph.java | 46 ++++++++------------------------ src/MetadataView.java | 35 ++++++++++++++++++++++++ src/Reader.java | 2 +- 9 files changed, 248 insertions(+), 48 deletions(-) create mode 100644 src/FamilyButton.java create mode 100644 src/FamilyButtonListener.java create mode 100644 src/MetadataView.java diff --git a/src/CSVReader.java b/src/CSVReader.java index da11f36..62e7845 100644 --- a/src/CSVReader.java +++ b/src/CSVReader.java @@ -10,7 +10,7 @@ import java.util.Arrays; * References: * * Date: - * 2024-05-20 + * 2024-05-25 * * Purpose of class: * Read from and interpret CSV files. diff --git a/src/ComparisonView.java b/src/ComparisonView.java index e1104ef..636cd18 100644 --- a/src/ComparisonView.java +++ b/src/ComparisonView.java @@ -16,7 +16,7 @@ import java.awt.*; * * * Date: - * 2024-05-20 + * 2024-05-25 * * Purpose of class: * Provide a view for comparing font families. @@ -28,7 +28,10 @@ public class ComparisonView extends JFrame { private final int WINDOW_MIN_HEIGHT = 540; // A ComparisonView has a minimum width private ArrayList fonts; // A ComparisonView has a list of fonts private Font textFont; // A ComparisonView has a preferred text font + private Font buttonFont; // A ComparisonView has a preferred button font private final int TEXT_SIZE = 20; // A ComparisonView has a constant text size. + private FamilyButton buttons[]; // A ComparisonView has an array of buttons. + private final int BUTTON_MAX = 10; // A ComparisonView has a set maximum number of buttons. /** * Walk the file tree. @@ -70,6 +73,10 @@ public class ComparisonView extends JFrame { getContentPane().setFont(textFont); UIManager.put("Label.font", textFont); UIManager.put("Panel.font", textFont); + UIManager.put("TextArea.font", textFont); + /* Set button font to a DejaVu Sans Condensed at a smaller point size. */ + buttonFont = new Font("DejaVu Sans Condensed", Font.PLAIN, TEXT_SIZE * 3 / 4); + UIManager.put("ToggleButton.font", buttonFont); setTitle("Google Fonts Style vs. Popularity"); // Window title @@ -121,14 +128,37 @@ public class ComparisonView extends JFrame { fonts.add(new FontFamily(metadata, popularity, style)); } + /* Sort fonts by their views over the past 30 days. */ + fonts = FontFamily.sort(fonts, "30day"); + waiting.remove(waitingLabel); // Remove waiting message. - JPanel author = new JPanel(); - JLabel authorLabel = new JLabel("Written by Kian Agheli"); - author.add(authorLabel); - add(author, BorderLayout.SOUTH); // Add author panel. + + /* Add panel for font family metadata. */ + JPanel metadataPanel = new JPanel(); + MetadataView metadataView = new MetadataView(); + metadataPanel.add(metadataView); + this.add(metadataPanel, BorderLayout.WEST); LineGraph graph = new LineGraph(fonts); // Draw a line graph of the font views metadata. - add(graph, BorderLayout.NORTH); // Add the line graph. + add(graph, BorderLayout.EAST); // Add the line graph. + + /* Add panel for font family metadata activation buttons. BUTTON_MAX buttons + in horizontal orientation. */ + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new GridLayout(1, BUTTON_MAX)); + buttons = new FamilyButton[BUTTON_MAX]; + /* Display buttons for the top families. */ + for (int i = 0; i < BUTTON_MAX; i++) { + /* Get the family associated with the given iteration's sort order. */ + FamilyButton button = new FamilyButton(fonts.get(i)); + /* Set the text of the button to the name of the font family. */ + button.setText(button.getFamily().getFamilyName()); + button.setBackground(new Color(LineGraph.getColors()[i])); + button.addItemListener(new FamilyButtonListener(buttons, i, metadataView)); + buttons[i] = button; + buttonPanel.add(button); + } + this.add(buttonPanel, BorderLayout.SOUTH); pack(); // Pack the window. setVisible(true); // Re-draw the window. diff --git a/src/FamilyButton.java b/src/FamilyButton.java new file mode 100644 index 0000000..994e848 --- /dev/null +++ b/src/FamilyButton.java @@ -0,0 +1,31 @@ +import javax.swing.JToggleButton; + +/* + * @author + * Kian Agheli + * + * References: + * https://www.geeksforgeeks.org/java-swing-jtogglebutton-class/ + * + * Date: + * 2024-05-25 + * + * Purpose of class: + * Provide an interative button to toggle FontFamily metadata. + */ + +public class FamilyButton extends JToggleButton { + private FontFamily family; // A FamilyButton has-a font family + + public FamilyButton(FontFamily family) + { + this.family = family; // Set family to provided + } + + /** + * Return the FontFamily associated with the instantiated button. + */ + public FontFamily getFamily() { + return family; + } +} diff --git a/src/FamilyButtonListener.java b/src/FamilyButtonListener.java new file mode 100644 index 0000000..95a4bc4 --- /dev/null +++ b/src/FamilyButtonListener.java @@ -0,0 +1,61 @@ +import java.awt.event.*; + +/* + * @author + * Kian Agheli + * + * References: + * + * Date: + * 2024-05-25 + * + * Purpose of class: + * Listen for state changes in an instantiation of FamilyButton. + */ +public class FamilyButtonListener implements ItemListener +{ + private MetadataView metadataView; // A FamilyButtonListener has a metadataView + private FamilyButton[] buttons; // A FamilyButtonListener has an array of buttons + private int buttonIndex; // A FamilyButtonListener has a button at a specified index in the array + + public FamilyButtonListener(FamilyButton[] buttons, int buttonIndex, MetadataView metadataView) { + /* Set each of the instance's fields to the given objects. */ + this.buttons = buttons; + this.buttonIndex = buttonIndex; + this.metadataView = metadataView; + } + + @Override + public void itemStateChanged(ItemEvent e) { + /* Get the state of the button. */ + int state = e.getStateChange(); + /* If the button is selected: */ + if (state == ItemEvent.SELECTED) { + /* Deselect all other buttons. */ + for (int i = 0; i < buttons.length; i++) { + if (i != buttonIndex) { + buttons[i].setSelected(false); + } + } + + /* Set the metadata on display to the metadata of the FontFamily + associated with the selected button. */ + metadataView.setText(buttons[buttonIndex].getFamily().toString()); + /* If the button is deselected: */ + } else { + /* Check if every button is deselected. */ + boolean selected = true; + for (int i = 0; i < buttons.length; i++) { + if (!buttons[i].isSelected()) { + selected = false; + } + } + + /* If every button is deselected, reset the metadata to display + the default text. */ + if (!selected) { + metadataView.reset(); + } + } + } +} diff --git a/src/FontFamily.java b/src/FontFamily.java index 97328d8..0b7c458 100644 --- a/src/FontFamily.java +++ b/src/FontFamily.java @@ -1,4 +1,7 @@ import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; /* @@ -8,7 +11,7 @@ import java.util.HashMap; * References: * * Date: - * 2024-05-20 + * 2024-05-25 * * Purpose of class: * Store and operate on font family metadata. @@ -153,4 +156,61 @@ public class FontFamily { public HashMap getViews() { return views; } + + /* Sort an ArrayList by a given metric of views, in descending order. */ + public static ArrayList sort(ArrayList fonts, String metric) { + Collections.sort(fonts, new Comparator() { + /* If a is lesser, return 1. If b is lesser, return -1. + Otherwise, they are equal, return 0. */ + public int compare (FontFamily a, FontFamily b) { + /* If equal, return 0. */ + if (a.getViews().get(metric) == b.getViews().get(metric)) { + return 0; + } + + /* If either is null, it is lesser. */ + if (a.getViews().get(metric) == null) { + return 1; + } + if (b.getViews().get(metric) == null) { + return -1; + } + + /* Neither are null, compare. */ + if (a.getViews().get(metric) > b.getViews().get(metric)) { + return -1; + } + + /* aViews must be lesser. */ + return 1; + } + }); + + return fonts; // Return sorted ArrayList. + } + + public String toString() { + String string = String.format("%s\nPublished %s\n", familyName, dateAdded); + string += "Styles:\n"; + for (String style : styles.keySet()) { + string += String.format(" %s: %d%%\n", style, styles.get(style)); + } + string += String.format("Designed by %s\n", designer); + string += String.format("Distributed under the terms of the %s\n", license); + string += String.format("Vaguely categorized as %s\n", category); + string += "Unicode subsets:\n"; + /* Print two subsets on each line. */ + for (int i = 0; i < subsets.length - 1; i++) { + if (i % 2 == 0) { + string += String.format(" %s", subsets[i]); + } else { + string += String.format("; %s\n", subsets[i]); + } + } + if (subsets.length % 2 == 0) { + string += '\n'; + } + + return string; + } } diff --git a/src/JSONReader.java b/src/JSONReader.java index a4af8c9..4cc72ad 100644 --- a/src/JSONReader.java +++ b/src/JSONReader.java @@ -12,9 +12,10 @@ import java.io.*; * https://stackoverflow.com/questions/3880274/how-to-convert-the-object-to-string-in-java * https://stackoverflow.com/questions/8938498/get-the-index-of-a-pattern-in-a-string-using-regex * https://howtodoinjava.com/java/regex/start-end-of-string/ + * https://www.baeldung.com/java-remove-last-character-of-string * * Date: - * 2024-05-20 + * 2024-05-25 * * Purpose of class: * Read from and interpret JSON files. @@ -77,9 +78,15 @@ public class JSONReader extends Reader { /* This JSON is pretty-printed. */ int nl = json.indexOf("\n", start); - /* Extract substring. Remove any quotation marks and commas. */ - String substring = json.substring(start, nl); - return substring.replaceAll("[\",]", ""); + /* Extract substring. Remove any quotation marks. */ + String extract = json.substring(start, nl); + extract = extract.replaceAll("\"", ""); + + /* If the string ends with a comma, remove it. */ + if (extract.endsWith(",")) { + extract = extract.substring(0, extract.length() - 1); + } + return extract; } /** diff --git a/src/LineGraph.java b/src/LineGraph.java index 7c4b8ac..7ef9a94 100644 --- a/src/LineGraph.java +++ b/src/LineGraph.java @@ -21,7 +21,7 @@ import java.util.Date; * https://stackoverflow.com/questions/5799140/java-get-month-string-from-integer * * Date: - * 2024-05-20 + * 2024-05-25 * * Purpose of class: * Draw a line graph. @@ -32,15 +32,17 @@ public class LineGraph extends JPanel { private ArrayList fonts; // A line graph has-a set of fonts private final int WIDTH = 640; // A line graph has a preferred width private final int HEIGHT = 480; // A line graph has a preferred height - private final int LINES = 0x10; // A line graph has a maximum lines graphed at a time + private final int LINES = 10; // A line graph has a maximum lines graphed at a time private final int LINE_WIDTH = 3; // A line graph has lines with a given pixel width private final int TICK_HEIGHT = 10; // A line graph has ticks with a given pixel height /* A line graph has an array of abbreviated month names. */ private final String[] ABBREVIATED_MONTH = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; - /* A line graph has an array of unique color values to use for each of the lines. */ - private final int[] COLORS = {0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff, - 0xff8040, 0xff4080, 0x80ff40, 0x40ff80, 0x4080ff, 0x8040ff, 0xff8080, 0x80ff80, 0x8080ff, + /* A line graph has an array of unique color values to use for each of the lines. + Made static so that it may be referenced from ComparisonView, so that the colors used for line + drawings and their associated buttons may match. */ + private static final int[] COLORS = {0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff, + 0xff8040, 0xff4080, 0x4080ff, 0xff8080, 0x80ff80, 0x8080ff, 0xff4040, 0x40ff40, 0x4040ff}; /** @@ -111,8 +113,6 @@ public class LineGraph extends JPanel { graphics.drawLine(TICK_HEIGHT + xOffset - LINE_WIDTH / 3, 0, TICK_HEIGHT + xOffset - LINE_WIDTH / 3, yMax); - sortFonts("30day"); // Sort the list of fonts by their 30day metrics. - int y = 0 + metrics.getHeight(); /* Cast to double within parentheses for floating-point division, cast back to int to remove fractional part. @@ -187,33 +187,9 @@ public class LineGraph extends JPanel { } } - /* Sort by views, in descending order. */ - private void sortFonts(String metric) { - Collections.sort(fonts, new Comparator() { - /* If a is lesser, return 1. If b is lesser, return -1. - Otherwise, they are equal, return 0. */ - public int compare (FontFamily a, FontFamily b) { - /* If equal, return 0. */ - if (a.getViews().get(metric) == b.getViews().get(metric)) { - return 0; - } - - /* If either is null, it is lesser. */ - if (a.getViews().get(metric) == null) { - return 1; - } - if (b.getViews().get(metric) == null) { - return -1; - } - - /* Neither are null, compare. */ - if (a.getViews().get(metric) > b.getViews().get(metric)) { - return -1; - } - - /* aViews must be lesser. */ - return 1; - } - }); + /* Return the array of hex values used to color the lines on the graph. + The background color of each FamilyButton match the associated line on the graph. */ + public static int[] getColors() { + return COLORS; } } diff --git a/src/MetadataView.java b/src/MetadataView.java new file mode 100644 index 0000000..5837aca --- /dev/null +++ b/src/MetadataView.java @@ -0,0 +1,35 @@ +import javax.swing.JTextArea; +import java.awt.*; + +/* + * @author + * Kian Agheli + * + * References: + * https://docs.oracle.com/javase/tutorial/uiswing/components/textarea.html + * + * Date: + * 2024-05-25 + * + * Purpose of class: + * Provide an area to show metadata. + */ + +public class MetadataView extends JTextArea { + // A MetadataView has-a default text. + private final String defaultText = "Toggle a font family using a colorful button below.\n" + + "Each color corresponds with a line on the graph to the right.\n" + + "The graph tracks monthly views."; + + public MetadataView() { + super(); // Call parent constructor + setEditable(false); // Disable editing of the widget. + setFocusable(false); // Disable focusing of the widget. + setText(defaultText); // Set to the default text. + } + + /* Reset the MetadataView. Set the text to the default. */ + public void reset() { + setText(defaultText); + } +} diff --git a/src/Reader.java b/src/Reader.java index d4a46fa..d066d6d 100644 --- a/src/Reader.java +++ b/src/Reader.java @@ -11,7 +11,7 @@ import java.util.List; * https://www.baeldung.com/java-scanner * * Date: - * 2024-05-20 + * 2024-05-25 * * Purpose of class: * Read from a file. -- cgit v1.2.3