From 5776fa7f94178bef7ae43fd24d9b73ed4eca4703 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Wed, 10 Mar 2021 23:36:54 +0100 Subject: [PATCH] Tried adding some sort of document filter. --- .../blockbookbinder/BlockBookBinder.java | 2 +- .../control/CleanSourceActionListener.java | 45 ----------------- .../control/ConvertToBookActionListener.java | 29 ----------- .../blockbookbinder/model/Book.java | 5 ++ .../blockbookbinder/model/BookPage.java | 18 +++++++ .../model/build/BookBuilder.java | 19 ++++++- .../{model => util}/CharWidthMapper.java | 18 ++++++- .../blockbookbinder/view/MainFrame.java | 6 +-- .../blockbookbinder/view/SourceTextPanel.java | 49 ------------------- .../view/book/BookPageDocumentFilter.java | 16 ++++++ .../view/book/BookPreviewPanel.java | 15 ++++-- src/main/resources/application.properties | 2 +- 12 files changed, 86 insertions(+), 138 deletions(-) delete mode 100644 src/main/java/nl/andrewlalis/blockbookbinder/control/CleanSourceActionListener.java delete mode 100644 src/main/java/nl/andrewlalis/blockbookbinder/control/ConvertToBookActionListener.java rename src/main/java/nl/andrewlalis/blockbookbinder/{model => util}/CharWidthMapper.java (77%) delete mode 100644 src/main/java/nl/andrewlalis/blockbookbinder/view/SourceTextPanel.java diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/BlockBookBinder.java b/src/main/java/nl/andrewlalis/blockbookbinder/BlockBookBinder.java index a31fda5..2f2d586 100644 --- a/src/main/java/nl/andrewlalis/blockbookbinder/BlockBookBinder.java +++ b/src/main/java/nl/andrewlalis/blockbookbinder/BlockBookBinder.java @@ -10,8 +10,8 @@ import javax.swing.*; */ public class BlockBookBinder { public static void main(String[] args) { + FlatDarkLaf.install(); SwingUtilities.invokeLater(() -> { - FlatDarkLaf.install(); var mainFrame = new MainFrame(); mainFrame.setupAndShow(); }); diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/control/CleanSourceActionListener.java b/src/main/java/nl/andrewlalis/blockbookbinder/control/CleanSourceActionListener.java deleted file mode 100644 index 794adf6..0000000 --- a/src/main/java/nl/andrewlalis/blockbookbinder/control/CleanSourceActionListener.java +++ /dev/null @@ -1,45 +0,0 @@ -package nl.andrewlalis.blockbookbinder.control; - -import nl.andrewlalis.blockbookbinder.view.SourceTextPanel; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -/** - * Listener for an action where the source text is 'cleaned' by removing any - * trailing whitespace, and removing unnecessary newlines. - */ -public class CleanSourceActionListener implements ActionListener { - private final SourceTextPanel sourceTextPanel; - - public CleanSourceActionListener(SourceTextPanel sourceTextPanel) { - this.sourceTextPanel = sourceTextPanel; - } - - @Override - public void actionPerformed(ActionEvent e) { - final String source = this.sourceTextPanel.getSourceText(); - String updated = source.trim() - .replaceAll("(?>\\v)+(\\v)", "\n\n") // Replace large chunks of newline with just two. - .replace(" ", " "); // Remove any double spaces. - updated = this.removeNewlineWrapping(updated); - this.sourceTextPanel.setSourceText(updated); - } - - private String removeNewlineWrapping(String source) { - final StringBuilder sb = new StringBuilder(source.length()); - final char[] sourceChars = source.toCharArray(); - for (int i = 0; i < sourceChars.length; i++) { - char c = sourceChars[i]; - if ( - c == '\n' - && (i - 1 >= 0 && !Character.isWhitespace(sourceChars[i - 1])) - && (i + 1 < sourceChars.length && !Character.isWhitespace(sourceChars[i + 1])) - ) { - c = ' '; - } - sb.append(c); - } - return sb.toString(); - } -} diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/control/ConvertToBookActionListener.java b/src/main/java/nl/andrewlalis/blockbookbinder/control/ConvertToBookActionListener.java deleted file mode 100644 index b2b1c73..0000000 --- a/src/main/java/nl/andrewlalis/blockbookbinder/control/ConvertToBookActionListener.java +++ /dev/null @@ -1,29 +0,0 @@ -package nl.andrewlalis.blockbookbinder.control; - -import nl.andrewlalis.blockbookbinder.model.build.BookBuilder; -import nl.andrewlalis.blockbookbinder.view.book.BookPreviewPanel; -import nl.andrewlalis.blockbookbinder.view.SourceTextPanel; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -/** - * Action listener that, when activated, converts the text from the source panel - * into a formatted book. - */ -public class ConvertToBookActionListener implements ActionListener { - private final SourceTextPanel sourceTextPanel; - private final BookPreviewPanel bookPreviewPanel; - - public ConvertToBookActionListener(SourceTextPanel sourceTextPanel, BookPreviewPanel bookPreviewPanel) { - this.sourceTextPanel = sourceTextPanel; - this.bookPreviewPanel = bookPreviewPanel; - } - - @Override - public void actionPerformed(ActionEvent e) { - this.bookPreviewPanel.setBook( - new BookBuilder().build(this.sourceTextPanel.getSourceText()) - ); - } -} diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/model/Book.java b/src/main/java/nl/andrewlalis/blockbookbinder/model/Book.java index 290dab1..4ad9330 100644 --- a/src/main/java/nl/andrewlalis/blockbookbinder/model/Book.java +++ b/src/main/java/nl/andrewlalis/blockbookbinder/model/Book.java @@ -22,6 +22,11 @@ public class Book { this.pages.add(page); } + public BookPage getPage(int index) { + if (index < 0 || index >= this.pages.size()) return null; + return this.pages.get(index); + } + /** * Gets a book containing the pages specified by the range. * @param firstIndex The index of the first page to include. diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/model/BookPage.java b/src/main/java/nl/andrewlalis/blockbookbinder/model/BookPage.java index d246466..2aa0afd 100644 --- a/src/main/java/nl/andrewlalis/blockbookbinder/model/BookPage.java +++ b/src/main/java/nl/andrewlalis/blockbookbinder/model/BookPage.java @@ -20,6 +20,24 @@ public class BookPage { return true; } + public String getLine(int index) { + if (index < 0 || index >= this.lines.size()) { + return null; + } + return this.lines.get(index); + } + + public int getLineIndexAtOffset(int offset) { + int lineIndex = 0; + String line = this.getLine(lineIndex); + if (line == null) return -1; + while (offset - line.length() > 0) { + offset-= line.length(); + line = this.getLine(++lineIndex); + } + return lineIndex; + } + public boolean hasContent() { return !this.lines.isEmpty(); } diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/model/build/BookBuilder.java b/src/main/java/nl/andrewlalis/blockbookbinder/model/build/BookBuilder.java index c9bd747..9240354 100644 --- a/src/main/java/nl/andrewlalis/blockbookbinder/model/build/BookBuilder.java +++ b/src/main/java/nl/andrewlalis/blockbookbinder/model/build/BookBuilder.java @@ -2,7 +2,7 @@ package nl.andrewlalis.blockbookbinder.model.build; import nl.andrewlalis.blockbookbinder.model.Book; import nl.andrewlalis.blockbookbinder.model.BookPage; -import nl.andrewlalis.blockbookbinder.model.CharWidthMapper; +import nl.andrewlalis.blockbookbinder.util.CharWidthMapper; import nl.andrewlalis.blockbookbinder.util.ApplicationProperties; import java.util.ArrayList; @@ -16,7 +16,7 @@ public class BookBuilder { */ public Book build(String source) { final int maxLines = ApplicationProperties.getIntProp("book.page_max_lines"); - List lines = this.convertSourceToLines(source); + List lines = this.convertSourceToLines(this.cleanSource(source)); Book book = new Book(); BookPage page = new BookPage(); int currentPageLineCount = 0; @@ -38,6 +38,21 @@ public class BookBuilder { return book; } + /** + * Cleans a given source text, by removing superfluous newlines that won't + * look too nice in minecraft text, and by erasing manually-imposed single + * newlines that may be in the source text to format it in a normal editor. + * @param source The source text. + * @return The cleaned string. + */ + public String cleanSource(String source) { + return source.trim() + .replaceAll("(?>\\v)+(\\v)", "\n\n") // Replace large chunks of newline with just two. + .replaceAll("\\S\n\\S", " ") // Unwrap previously-imposed single-line wrapping. + .replaceAll("\t", " ") // Replace tabs with single-spaces, due to space constraints. + .replaceAll(" [ ]+", " "); // Remove any superfluous spaces. + } + /** * Converts the given source string into a formatted list of lines that can * be copied to a minecraft book. diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/model/CharWidthMapper.java b/src/main/java/nl/andrewlalis/blockbookbinder/util/CharWidthMapper.java similarity index 77% rename from src/main/java/nl/andrewlalis/blockbookbinder/model/CharWidthMapper.java rename to src/main/java/nl/andrewlalis/blockbookbinder/util/CharWidthMapper.java index ad5351b..fe6c2f7 100644 --- a/src/main/java/nl/andrewlalis/blockbookbinder/model/CharWidthMapper.java +++ b/src/main/java/nl/andrewlalis/blockbookbinder/util/CharWidthMapper.java @@ -1,11 +1,14 @@ -package nl.andrewlalis.blockbookbinder.model; +package nl.andrewlalis.blockbookbinder.util; import lombok.Getter; -import nl.andrewlalis.blockbookbinder.util.ApplicationProperties; import java.util.HashMap; import java.util.Map; +/** + * This class provides the ability to determine the length of a string, in + * pixels, according to Minecraft's internal font. + */ public class CharWidthMapper { @Getter private static final CharWidthMapper instance = new CharWidthMapper(); @@ -21,6 +24,16 @@ public class CharWidthMapper { return this.charWidthMap.getOrDefault(c, 6); } + public int getWidth(String s) { + if (s.length() == 0) return 0; + int width = getWidth(s.charAt(0)); + for (int i = 1; i < s.length(); i++) { + final char c = s.charAt(i); + width += this.getWidth(c) + 1; + } + return width; + } + private void initCharWidthMap() { this.charWidthMap.put(' ', 3); this.charWidthMap.put('!', 1); @@ -49,6 +62,7 @@ public class CharWidthMapper { this.charWidthMap.put('|', 1); this.charWidthMap.put('}', 3); this.charWidthMap.put('~', 6); + this.charWidthMap.put('\n', 0); final int defaultWidth = ApplicationProperties.getIntProp("book.default_char_width"); for (char c = 32; c < 127; c++) { diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/view/MainFrame.java b/src/main/java/nl/andrewlalis/blockbookbinder/view/MainFrame.java index 53df354..09521d8 100644 --- a/src/main/java/nl/andrewlalis/blockbookbinder/view/MainFrame.java +++ b/src/main/java/nl/andrewlalis/blockbookbinder/view/MainFrame.java @@ -58,12 +58,8 @@ public class MainFrame extends JFrame { private Container buildContentPane() { JPanel mainPanel = new JPanel(new BorderLayout()); - JPanel doublePanel = new JPanel(new GridLayout(1, 2)); BookPreviewPanel bookPreviewPanel = new BookPreviewPanel(); - doublePanel.add(bookPreviewPanel); - SourceTextPanel sourceTextPanel = new SourceTextPanel(bookPreviewPanel); - doublePanel.add(sourceTextPanel); - mainPanel.add(doublePanel, BorderLayout.CENTER); + mainPanel.add(bookPreviewPanel, BorderLayout.CENTER); JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); JButton exportButton = new JButton("Export to Book"); diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/view/SourceTextPanel.java b/src/main/java/nl/andrewlalis/blockbookbinder/view/SourceTextPanel.java deleted file mode 100644 index 8ca6319..0000000 --- a/src/main/java/nl/andrewlalis/blockbookbinder/view/SourceTextPanel.java +++ /dev/null @@ -1,49 +0,0 @@ -package nl.andrewlalis.blockbookbinder.view; - -import nl.andrewlalis.blockbookbinder.control.CleanSourceActionListener; -import nl.andrewlalis.blockbookbinder.control.ConvertToBookActionListener; -import nl.andrewlalis.blockbookbinder.view.book.BookPreviewPanel; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import java.awt.*; - -/** - * A panel dedicated to displaying an interacting with a raw source of text for - * a book. - */ -public class SourceTextPanel extends JPanel { - private final JTextArea textArea; - - public SourceTextPanel(BookPreviewPanel bookPreviewPanel) { - super(new BorderLayout()); - - this.add(new JLabel("Source Text"), BorderLayout.NORTH); - this.setBorder(new EmptyBorder(5, 5, 5, 5)); - - this.textArea = new JTextArea(); - this.textArea.setWrapStyleWord(true); - this.textArea.setLineWrap(true); - JScrollPane scrollWrappedMainTextArea = new JScrollPane(this.textArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - this.add(scrollWrappedMainTextArea, BorderLayout.CENTER); - - JPanel rightPanelButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - this.add(rightPanelButtonPanel, BorderLayout.SOUTH); - - JButton convertButton = new JButton("Convert to Book"); - convertButton.addActionListener(new ConvertToBookActionListener(this, bookPreviewPanel)); - rightPanelButtonPanel.add(convertButton); - - JButton cleanButton = new JButton("Clean"); - cleanButton.addActionListener(new CleanSourceActionListener(this)); - rightPanelButtonPanel.add(cleanButton); - } - - public String getSourceText() { - return this.textArea.getText(); - } - - public void setSourceText(String text) { - this.textArea.setText(text); - } -} diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/view/book/BookPageDocumentFilter.java b/src/main/java/nl/andrewlalis/blockbookbinder/view/book/BookPageDocumentFilter.java index 0acc323..a15b6fd 100644 --- a/src/main/java/nl/andrewlalis/blockbookbinder/view/book/BookPageDocumentFilter.java +++ b/src/main/java/nl/andrewlalis/blockbookbinder/view/book/BookPageDocumentFilter.java @@ -1,23 +1,39 @@ package nl.andrewlalis.blockbookbinder.view.book; +import lombok.Setter; +import nl.andrewlalis.blockbookbinder.model.BookPage; + import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.DocumentFilter; public class BookPageDocumentFilter extends DocumentFilter { + @Setter + private BookPage page; @Override public void remove(FilterBypass fb, int offset, int length) throws BadLocationException { + System.out.printf("Remove: offset=%d, length=%d\n", offset, length); + if (page == null) return; super.remove(fb, offset, length); } @Override public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException { + System.out.printf("Insert: offset=%d, string=%s\n", offset, string); + if (page == null) return; + int lineIndex = page.getLineIndexAtOffset(offset); + System.out.printf("Insert on line %d: %s\n", lineIndex, page.getLine(lineIndex)); + fb.getDocument().getStartPosition().getOffset(); super.insertString(fb, offset, string, attr); } @Override public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { + System.out.printf("Replace: offset=%d, length=%d, text=%s\n", offset, length, text); + if (page == null) return; + int lineIndex = page.getLineIndexAtOffset(offset); + System.out.printf("Replace on line %d: %s\n", lineIndex, page.getLine(lineIndex)); super.replace(fb, offset, length, text, attrs); } } diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/view/book/BookPreviewPanel.java b/src/main/java/nl/andrewlalis/blockbookbinder/view/book/BookPreviewPanel.java index f7b5299..a85796c 100644 --- a/src/main/java/nl/andrewlalis/blockbookbinder/view/book/BookPreviewPanel.java +++ b/src/main/java/nl/andrewlalis/blockbookbinder/view/book/BookPreviewPanel.java @@ -6,6 +6,7 @@ import nl.andrewlalis.blockbookbinder.model.BookPage; import javax.swing.*; import javax.swing.border.EmptyBorder; +import javax.swing.text.AbstractDocument; import java.awt.*; import java.io.IOException; import java.io.InputStream; @@ -19,6 +20,7 @@ public class BookPreviewPanel extends JPanel { private int currentPage = 0; private final JTextArea previewPageTextArea; + private final BookPageDocumentFilter documentFilter; private final JLabel titleLabel; private final JButton previousPageButton; @@ -34,7 +36,10 @@ public class BookPreviewPanel extends JPanel { this.setBorder(new EmptyBorder(5, 5, 5, 5)); this.previewPageTextArea = new JTextArea(); - this.previewPageTextArea.setEditable(false); + this.documentFilter = new BookPageDocumentFilter(); + AbstractDocument doc = (AbstractDocument) this.previewPageTextArea.getDocument(); + doc.setDocumentFilter(this.documentFilter); + this.previewPageTextArea.setEditable(true); try { InputStream is = this.getClass().getClassLoader().getResourceAsStream("fonts/1_Minecraft-Regular.otf"); if (is == null) { @@ -84,7 +89,9 @@ public class BookPreviewPanel extends JPanel { previewButtonPanel.add(this.lastPageButton); this.add(previewButtonPanel, BorderLayout.SOUTH); - this.setBook(new Book()); + Book starterBook = new Book(); + starterBook.addPage(new BookPage()); + this.setBook(starterBook); } private void displayCurrentPage() { @@ -98,12 +105,12 @@ public class BookPreviewPanel extends JPanel { public void setBook(Book book) { this.book = book; - this.currentPage = 0; - this.displayCurrentPage(); + this.setCurrentPage(0); } public void setCurrentPage(int page) { this.currentPage = page; + this.documentFilter.setPage(this.book.getPage(page)); this.displayCurrentPage(); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 7264766..57e3242 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,6 @@ # Settings for the application's GUI. frame.title=Block Book Binder -frame.default_width=800 +frame.default_width=500 frame.default_height=600 export_dialog.min_width=400