aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorj-james2022-11-26 05:47:38 +0000
committerj-james2022-11-26 05:47:38 +0000
commit39ff1356f08ee8068852b9b58362c06a9ef77e1e (patch)
tree7f9f4b170e871cca54675e49efda95da13a6c48b
parent402b25d25e91048e284513c19300cdad42054ff8 (diff)
Complete milestone P3
-rw-r--r--README.md9
-rw-r--r--src/main/ui/BrowserBar.java98
-rw-r--r--src/main/ui/BrowserCanvas.java42
-rw-r--r--src/main/ui/BrowserWindow.java139
-rw-r--r--src/main/ui/Main.java2
5 files changed, 289 insertions, 1 deletions
diff --git a/README.md b/README.md
index f2b92b1..67147dc 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,15 @@ I've heard that Java Swing has a native HTML rendering component. I hope to enti
- As a user, I want to be given the option to save my currently open tabs to disk when quitting.
- As a user, I want to be given the option to restore my previous tabs upon relaunching the application.
+## instructions for grader
+
+- You can generate the first required event of adding multiple Xs to a Y by creating a new tab by entering a path into the browser bar and pressing "Go". Observe that the tab is added to the tablist.
+- You can generate the second required event of adding multiple Xs to a Y by creating a new tab by opening the tab menu, selecting a tab, and closing it. Observe that the tab is removed from the tablist.
+- The panel in which all of the Xs added to a Y are displayed is the tablist.
+- You can locate my visual component by observing the main browser window, which may render arbitrary paths and open tabs.
+- You can save the state of my application by attempting to close it with tabs open. You will be asked if you would like to save your tabs.
+- You can load the state of my application by attempting to open it after saving tabs. You will be asked if you would like to restore your tabs.
+
## credits
This project makes extensive use of the Javatuples library ([javatuples.org](https://www.javatuples.org/)).
diff --git a/src/main/ui/BrowserBar.java b/src/main/ui/BrowserBar.java
new file mode 100644
index 0000000..09c78a5
--- /dev/null
+++ b/src/main/ui/BrowserBar.java
@@ -0,0 +1,98 @@
+package ui;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.*;
+
+public class BrowserBar extends JToolBar {
+ private BrowserWindow parent;
+
+ private JPopupMenu tabMenu;
+ private JToggleButton tabButton;
+ private JTextField uriInput;
+// private JButton saveTabsButton;
+ private JButton openUriButton;
+
+ public BrowserBar(BrowserWindow parent) {
+ this.parent = parent;
+
+ var test = new JMenuItem("Ipsum");
+ tabMenu = new JPopupMenu("Tabs");
+ tabMenu.add(new JMenuItem("Hello"));
+ tabMenu.add(new JMenuItem("World"));
+ tabMenu.add(new JMenuItem("Lorem"));
+ tabMenu.add(test);
+ tabMenu.remove(test);
+
+ tabButton = new JToggleButton("Tabs");
+ tabButton.addActionListener(toggleTabMenu());
+ add(tabButton);
+
+ uriInput = new JTextField();
+ add(uriInput);
+
+ openUriButton = new JButton("Go");
+ openUriButton.addActionListener(openTab());
+ add(openUriButton);
+
+ }
+
+ // EFFECTS: opens the content of the text field in the current tab
+ private ActionListener openTab() {
+ return new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent actionEvent) {
+ String uri = uriInput.getText();
+ parent.render(uri);
+ addTab(uri);
+ System.out.println(uri);
+ System.out.println("should run");
+ }
+ };
+ }
+
+ // EFFECTS: adds a new tab pointing to URI in the background
+ public void addTab(String tab) {
+ JToggleButton tabButton = new JToggleButton(tab);
+
+ tabButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent actionEvent) {
+ int action = JOptionPane.showOptionDialog(null,
+ "Open or close this tab?", "apus",
+ JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE,
+ null, new String[]{"Open", "Close"}, "Open");
+ if (action == 0) {
+ parent.render(tab);
+ } else {
+ tabMenu.remove(tabButton);
+ tabMenu.setVisible(false);
+ parent.removeTab(tab);
+ }
+ }
+ });
+
+ this.tabMenu.add(tabButton);
+ parent.addTab(tab);
+ }
+
+ // MODIFIES: this
+ // EFFECTS: toggles the tab menu
+ private ActionListener toggleTabMenu() {
+ return new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent actionEvent) {
+ if (tabButton.isSelected()) {
+ Point location = tabButton.getLocationOnScreen();
+ location.translate(0, 30); // fuck this method lol
+ tabMenu.setLocation(location);
+ tabMenu.setVisible(true);
+ } else {
+ tabMenu.setVisible(false);
+ }
+ }
+ };
+ }
+}
diff --git a/src/main/ui/BrowserCanvas.java b/src/main/ui/BrowserCanvas.java
new file mode 100644
index 0000000..2a77442
--- /dev/null
+++ b/src/main/ui/BrowserCanvas.java
@@ -0,0 +1,42 @@
+package ui;
+
+import model.html.ElementNode;
+import model.html.TextNode;
+import model.util.Node;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.*;
+
+public class BrowserCanvas extends JPanel {
+ private ArrayList<Node> html;
+
+ // MODIFIES: this
+ // EFFECTS: constructs a BrowserCanvas object
+ public BrowserCanvas(ArrayList<Node> html) {
+ super();
+ this.html = html;
+ }
+
+ @Override
+ public void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ Point location = new Point(10, 20); // we need a mutable reference
+ renderHtml(html, g, location);
+ }
+
+ // EFFECTS: naively renders our html file by printing text nodes
+ private void renderHtml(ArrayList<Node> html, Graphics g, Point location) {
+ for (Node node : html) {
+ if (node instanceof TextNode) {
+ if (!node.getData().isBlank()) {
+ g.drawString(node.getData(), location.x, location.y);
+ location.translate(0, 20);
+ }
+ } else {
+ renderHtml(((ElementNode) node).getChildren(), g, location);
+ }
+ }
+ }
+
+}
diff --git a/src/main/ui/BrowserWindow.java b/src/main/ui/BrowserWindow.java
new file mode 100644
index 0000000..9a92186
--- /dev/null
+++ b/src/main/ui/BrowserWindow.java
@@ -0,0 +1,139 @@
+package ui;
+
+import model.html.HtmlParser;
+import org.json.JSONArray;
+import persistance.JsonUtils;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+
+// Broad JFrame usage taken from here: https://docs.oracle.com/javase/tutorial/uiswing/components/frame.html
+public class BrowserWindow extends JFrame {
+ public static final int WIDTH = 1200;
+ public static final int HEIGHT = 800;
+ private static final String storagePath = "data/apus.cache";
+
+ private BrowserCanvas canvas;
+ private BrowserBar browserBar;
+
+ private ArrayDeque<String> tabs;
+ private String pathString;
+
+ // MODIFIES: this
+ // EFFECTS: creates a new BrowserWindow program for rendering pages
+ public BrowserWindow() {
+ super("apus");
+ tabs = new ArrayDeque<>();
+ canvas = new BrowserCanvas(new ArrayList<>());
+// render("data/example.html");
+ browserBar = new BrowserBar(this);
+ getContentPane().add(browserBar, BorderLayout.SOUTH);
+// pack();
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setSize(WIDTH, HEIGHT);
+ render("/home/apropos/Projects/website/j-james/index.html");
+// browserBar.addTab("/home/apropos/Projects/website/j-james/index.html");
+ setVisible(true);
+ setClosingBehavior();
+
+ initializeBrowser();
+ }
+
+ // MODIFIES: this
+ // EFFECTS: Renders an arbitrary page
+ public void render(String uri) {
+ pathString = uri;
+ remove(canvas);
+ System.out.println(pathString);
+ try {
+ Path path = Paths.get(pathString);
+ String file = new String(Files.readAllBytes(path));
+ HtmlParser parser = new HtmlParser();
+ canvas = new BrowserCanvas(parser.parseHtml(file));
+ } catch (Exception e) {
+ System.out.println("Could not read file, rendering empty page: " + e.getMessage());
+
+ canvas = new BrowserCanvas(new ArrayList<>());
+ }
+ add(canvas);
+ repaint();
+ setVisible(true);
+ }
+
+ // EFFECTS: Prompts the user to save their tabs before quitting
+ private void setClosingBehavior() {
+ this.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent e) {
+ if (tabs.size() > 0) {
+ saveCurrentTabs();
+ }
+ super.windowClosing(e);
+ }
+ });
+ }
+
+ // EFFECTS: sets up the browser upon launching
+ private void initializeBrowser() {
+ if (new File(storagePath).length() > 2) {
+ restorePreviousTabs();
+ }
+ }
+
+ // MODIFIES: this
+ // EFFECTS: prompts the user to restore their previous tabs
+ private void restorePreviousTabs() {
+ int answer = JOptionPane.showOptionDialog(
+ this, "Would you like to restore your previous tabs?", "apus",
+ JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE,
+ null, new String[]{"Yes", "No"}, "Yes");
+ if (answer == 0) {
+ try {
+ JSONArray state = JsonUtils.readFromFile(storagePath);
+ for (int i = 0; i < state.length(); i++) {
+ this.browserBar.addTab((String) state.get(i));
+ this.addTab((String) state.get(i));
+ }
+ } catch (Exception e) {
+ System.out.println("Restoring state from disk failed with " + e.toString());
+ }
+ }
+ }
+
+ // EFFECTS: prompts the user to save their current tabs before closing
+ private void saveCurrentTabs() {
+ int answer = JOptionPane.showOptionDialog(
+ this, "Would you like to save your current tabs?", "apus",
+ JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE,
+ null, new String[]{"Yes", "No"}, "Yes");
+ if (answer == 0) {
+ JsonUtils.writeToFile(new JSONArray(tabs), storagePath);
+ } else {
+ JsonUtils.writeToFile(new JSONArray(), storagePath);
+ }
+ }
+
+ public ArrayDeque<String> getTabs() {
+ return tabs;
+ }
+
+ // MODIFIES: this
+ // EFFECTS: add a tab
+ public void addTab(String tab) {
+ this.tabs.add(tab);
+ }
+
+ // MODIFIES: this
+ // EFFECTS: remove a tab
+ public void removeTab(String tab) {
+ this.tabs.remove(tab);
+ }
+}
diff --git a/src/main/ui/Main.java b/src/main/ui/Main.java
index 841576f..267d65d 100644
--- a/src/main/ui/Main.java
+++ b/src/main/ui/Main.java
@@ -2,6 +2,6 @@ package ui;
public class Main {
public static void main(String[] args) {
- new BrowserApp();
+ new BrowserWindow();
}
}