Made new book builder algorithm work, cleaned up export dialog.

This commit is contained in:
Andrew Lalis 2023-07-09 20:18:48 -04:00
parent e32976fb8b
commit 605d050783
8 changed files with 132 additions and 44 deletions

View File

@ -3,6 +3,8 @@ package nl.andrewlalis.blockbookbinder.control.source;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import nl.andrewlalis.blockbookbinder.model.build.BookBuilder; import nl.andrewlalis.blockbookbinder.model.build.BookBuilder;
import nl.andrewlalis.blockbookbinder.model.build.BookBuilder2;
import nl.andrewlalis.blockbookbinder.util.ApplicationProperties;
import nl.andrewlalis.blockbookbinder.view.SourceTextPanel; import nl.andrewlalis.blockbookbinder.view.SourceTextPanel;
import nl.andrewlalis.blockbookbinder.view.book.BookPreviewPanel; import nl.andrewlalis.blockbookbinder.view.book.BookPreviewPanel;

View File

@ -0,0 +1,37 @@
package nl.andrewlalis.blockbookbinder.control.source;
import lombok.Getter;
import lombok.Setter;
import nl.andrewlalis.blockbookbinder.model.build.BookBuilder2;
import nl.andrewlalis.blockbookbinder.util.ApplicationProperties;
import nl.andrewlalis.blockbookbinder.view.SourceTextPanel;
import nl.andrewlalis.blockbookbinder.view.book.BookPreviewPanel;
import javax.swing.*;
import java.awt.event.ActionEvent;
public class CompileFromSourceAction2 extends AbstractAction {
@Getter
private static final CompileFromSourceAction2 instance = new CompileFromSourceAction2();
@Setter
private SourceTextPanel sourceTextPanel;
@Setter
private BookPreviewPanel bookPreviewPanel;
public CompileFromSourceAction2() {
super("Compile From Source 2");
this.putValue(SHORT_DESCRIPTION, "Compile the current source text into a book.");
}
@Override
public void actionPerformed(ActionEvent e) {
this.bookPreviewPanel.setBook(
new BookBuilder2(
ApplicationProperties.getIntProp("book.page_max_lines"),
ApplicationProperties.getIntProp("book.page_max_chars"),
ApplicationProperties.getIntProp("book.page_max_width")
).addText(this.sourceTextPanel.getSourceText()).build()
);
}
}

View File

@ -17,8 +17,20 @@ public class CharWidthMapper {
this.initCharWidthMap(); this.initCharWidthMap();
} }
public int getWidth(char c) { public static int getWidth(char c) {
return this.charWidthMap.getOrDefault(c, 6); return instance.charWidthMap.getOrDefault(c, 6);
}
public static int getWidth(String s) {
if (s.length() == 0) return 0;
int width = 0;
for (int i = 0; i < s.length(); i++) {
width += getWidth(s.charAt(i));
if (i < s.length() - 1) {
width++;
}
}
return width;
} }
private void initCharWidthMap() { private void initCharWidthMap() {

View File

@ -58,7 +58,7 @@ public class BookBuilder {
sourceIndex++; sourceIndex++;
symbolBuilder.setLength(0); symbolBuilder.setLength(0);
symbolBuilder.append(c); symbolBuilder.append(c);
int symbolWidth = CharWidthMapper.getInstance().getWidth(c); int symbolWidth = CharWidthMapper.getWidth(c);
// Since there's a 1-pixel gap between characters, add it to the width if this isn't the first char. // Since there's a 1-pixel gap between characters, add it to the width if this isn't the first char.
if (lineBuilder.length() > 0) { if (lineBuilder.length() > 0) {
@ -86,7 +86,7 @@ public class BookBuilder {
) { ) {
char nextChar = sourceChars[sourceIndex]; char nextChar = sourceChars[sourceIndex];
symbolBuilder.append(nextChar); symbolBuilder.append(nextChar);
symbolWidth += 1 + CharWidthMapper.getInstance().getWidth(nextChar); symbolWidth += 1 + CharWidthMapper.getWidth(nextChar);
sourceIndex++; sourceIndex++;
} }
} }

View File

@ -12,13 +12,10 @@ public class BookBuilder2 {
private final int MAX_CHARS_PER_PAGE; private final int MAX_CHARS_PER_PAGE;
private final int MAX_LINE_PIXEL_WIDTH; private final int MAX_LINE_PIXEL_WIDTH;
private List<String> lines; private final List<String> lines;
private StringBuilder lineBuilder; private final StringBuilder lineBuilder;
private StringBuilder wordBuilder; private final StringBuilder wordBuilder;
private int currentLine;
private int currentLinePixelWidth;
private int currentWordPixelWidth;
public BookBuilder2(int maxLinesPerPage, int maxCharsPerPage, int maxLinePixelWidth) { public BookBuilder2(int maxLinesPerPage, int maxCharsPerPage, int maxLinePixelWidth) {
this.MAX_LINES_PER_PAGE = maxLinesPerPage; this.MAX_LINES_PER_PAGE = maxLinesPerPage;
@ -27,9 +24,6 @@ public class BookBuilder2 {
this.lines = new ArrayList<>(); this.lines = new ArrayList<>();
this.lineBuilder = new StringBuilder(64); this.lineBuilder = new StringBuilder(64);
this.wordBuilder = new StringBuilder(64); this.wordBuilder = new StringBuilder(64);
this.currentLine = 0;
this.currentLinePixelWidth = 0;
this.currentWordPixelWidth = 0;
} }
public BookBuilder2 addText(String text) { public BookBuilder2 addText(String text) {
@ -40,9 +34,37 @@ public class BookBuilder2 {
appendLine(); appendLine();
} else if (c == ' ' && lineBuilder.length() == 0) { } else if (c == ' ' && lineBuilder.length() == 0) {
continue; // Skip spaces at the start of lines. continue; // Skip spaces at the start of lines.
} else if (Character.isWhitespace(c)) {
if (CharWidthMapper.getWidth(lineBuilder.toString() + c) > MAX_LINE_PIXEL_WIDTH) {
appendLine();
if (c != ' ') {
lineBuilder.append(c);
}
} else {
lineBuilder.append(c);
}
} else { // Read a continuous word. } else { // Read a continuous word.
int charsRead = readWord(text, idx - 1); String word = readWord(text, idx - 1);
idx += charsRead - 1; idx += word.length() - 1;
if (CharWidthMapper.getWidth(lineBuilder + word) <= MAX_LINE_PIXEL_WIDTH) {
// Append the word if it'll fit completely.
lineBuilder.append(word);
} else if (CharWidthMapper.getWidth(word) <= MAX_LINE_PIXEL_WIDTH) {
// Go to the next line and put the word there, since it'll fit.
appendLine();
lineBuilder.append(word);
} else {
// The word is so large that it doesn't fit on a line on its own.
// Find the largest substring of the word that'll fit with a hyphen.
int subStringSize = word.length() - 2;
while (CharWidthMapper.getWidth(word.substring(0, subStringSize) + "-") > MAX_LINE_PIXEL_WIDTH) {
subStringSize--;
}
appendLine();
lineBuilder.append(word, 0, subStringSize).append('-');
appendLine();
lineBuilder.append(word.substring(subStringSize));
}
} }
} }
return this; return this;
@ -52,14 +74,28 @@ public class BookBuilder2 {
Book book = new Book(); Book book = new Book();
BookPage page = new BookPage(); BookPage page = new BookPage();
int currentPageLineCount = 0; int currentPageLineCount = 0;
int currentPageCharCount = 0;
// Flush anything remaining in lineBuilder to a final line.
if (lineBuilder.length() > 0) {
appendLine();
}
for (String line : lines) { for (String line : lines) {
if (currentPageCharCount + line.length() > MAX_CHARS_PER_PAGE) {
book.addPage(page);
page = new BookPage();
currentPageLineCount = 0;
currentPageCharCount = 0;
}
page.addLine(line); page.addLine(line);
currentPageLineCount++; currentPageLineCount++;
currentPageCharCount += line.length();
if (currentPageLineCount == MAX_LINES_PER_PAGE) { if (currentPageLineCount == MAX_LINES_PER_PAGE) {
book.addPage(page); book.addPage(page);
page = new BookPage(); page = new BookPage();
currentPageLineCount = 0; currentPageLineCount = 0;
currentPageCharCount = 0;
} }
} }
if (page.hasContent()) { if (page.hasContent()) {
@ -68,32 +104,22 @@ public class BookBuilder2 {
return book; return book;
} }
private int readWord(String text, int firstCharIdx) { private String readWord(String text, int firstCharIdx) {
currentWordPixelWidth = 0;
wordBuilder.setLength(0); wordBuilder.setLength(0);
int idx = firstCharIdx; int idx = firstCharIdx;
while (idx < text.length()) { while (idx < text.length()) {
char c = text.charAt(idx++); char c = text.charAt(idx++);
if (!Character.isWhitespace(c)) { if (!Character.isWhitespace(c)) {
currentWordPixelWidth += CharWidthMapper.getInstance().getWidth(c) + 1;
wordBuilder.append(c); wordBuilder.append(c);
// If we notice that our word will cause the current line to exceed max width, go to a newline.
if (currentLinePixelWidth + currentWordPixelWidth > MAX_LINE_PIXEL_WIDTH) {
appendLine();
}
} else { } else {
break; break;
} }
} }
String word = wordBuilder.toString(); return wordBuilder.toString();
return word.length();
} }
private void appendLine() { private void appendLine() {
this.lines.add(this.lineBuilder.toString()); this.lines.add(this.lineBuilder.toString());
this.lineBuilder.setLength(0); this.lineBuilder.setLength(0);
this.currentLine++;
this.currentLinePixelWidth = 0;
} }
} }

View File

@ -4,6 +4,7 @@ import nl.andrewlalis.blockbookbinder.BlockBookBinder;
import nl.andrewlalis.blockbookbinder.control.export.ExportBookToMinecraftAction; import nl.andrewlalis.blockbookbinder.control.export.ExportBookToMinecraftAction;
import nl.andrewlalis.blockbookbinder.control.source.CleanSourceAction; import nl.andrewlalis.blockbookbinder.control.source.CleanSourceAction;
import nl.andrewlalis.blockbookbinder.control.source.CompileFromSourceAction; import nl.andrewlalis.blockbookbinder.control.source.CompileFromSourceAction;
import nl.andrewlalis.blockbookbinder.control.source.CompileFromSourceAction2;
import nl.andrewlalis.blockbookbinder.control.source.ImportSourceAction; import nl.andrewlalis.blockbookbinder.control.source.ImportSourceAction;
import nl.andrewlalis.blockbookbinder.util.ApplicationProperties; import nl.andrewlalis.blockbookbinder.util.ApplicationProperties;
import nl.andrewlalis.blockbookbinder.view.about.AboutDialog; import nl.andrewlalis.blockbookbinder.view.about.AboutDialog;
@ -44,11 +45,13 @@ public class MainFrame extends JFrame {
BookPreviewPanel bookPreviewPanel = new BookPreviewPanel(); BookPreviewPanel bookPreviewPanel = new BookPreviewPanel();
doublePanel.add(bookPreviewPanel); doublePanel.add(bookPreviewPanel);
CompileFromSourceAction.getInstance().setBookPreviewPanel(bookPreviewPanel); CompileFromSourceAction.getInstance().setBookPreviewPanel(bookPreviewPanel);
CompileFromSourceAction2.getInstance().setBookPreviewPanel(bookPreviewPanel);
ExportBookToMinecraftAction.getInstance().setBookPreviewPanel(bookPreviewPanel); ExportBookToMinecraftAction.getInstance().setBookPreviewPanel(bookPreviewPanel);
SourceTextPanel sourceTextPanel = new SourceTextPanel(); SourceTextPanel sourceTextPanel = new SourceTextPanel();
doublePanel.add(sourceTextPanel); doublePanel.add(sourceTextPanel);
CompileFromSourceAction.getInstance().setSourceTextPanel(sourceTextPanel); CompileFromSourceAction.getInstance().setSourceTextPanel(sourceTextPanel);
CompileFromSourceAction2.getInstance().setSourceTextPanel(sourceTextPanel);
CleanSourceAction.getInstance().setSourceTextPanel(sourceTextPanel); CleanSourceAction.getInstance().setSourceTextPanel(sourceTextPanel);
mainPanel.add(doublePanel, BorderLayout.CENTER); mainPanel.add(doublePanel, BorderLayout.CENTER);
@ -68,6 +71,7 @@ public class MainFrame extends JFrame {
JMenu bookMenu = new JMenu("Book"); JMenu bookMenu = new JMenu("Book");
bookMenu.add(CompileFromSourceAction.getInstance()); bookMenu.add(CompileFromSourceAction.getInstance());
bookMenu.add(CompileFromSourceAction2.getInstance());
bookMenu.add(CleanSourceAction.getInstance()); bookMenu.add(CleanSourceAction.getInstance());
bookMenu.add(ExportBookToMinecraftAction.getInstance()); bookMenu.add(ExportBookToMinecraftAction.getInstance());
menuBar.add(bookMenu); menuBar.add(bookMenu);

View File

@ -51,25 +51,32 @@ public class ExportToBookDialog extends JDialog {
private Container buildContentPane() { private Container buildContentPane() {
JPanel mainPanel = new JPanel(new BorderLayout()); JPanel mainPanel = new JPanel(new BorderLayout());
JPanel setupPanel = new JPanel(); JPanel setupPanel = new JPanel(new GridBagLayout());
setupPanel.setLayout(new BoxLayout(setupPanel, BoxLayout.PAGE_AXIS)); String[] labels = {"", "First Page", "Last Page", "Auto-Paste Delay (Seconds)"};
this.autoCheckbox = new JCheckBox("Auto-paste", true); this.autoCheckbox = new JCheckBox("Auto-paste", true);
this.firstPageSpinner = new JSpinner(new SpinnerNumberModel(1, 1, this.book.getPageCount(), 1)); this.firstPageSpinner = new JSpinner(new SpinnerNumberModel(1, 1, this.book.getPageCount(), 1));
JPanel firstPageSpinnerPanel = new JPanel(new BorderLayout());
firstPageSpinnerPanel.add(new JLabel("First Page:"), BorderLayout.WEST);
firstPageSpinnerPanel.add(this.firstPageSpinner, BorderLayout.CENTER);
this.lastPageSpinner = new JSpinner(new SpinnerNumberModel(this.book.getPageCount(), 1, this.book.getPageCount(), 1)); this.lastPageSpinner = new JSpinner(new SpinnerNumberModel(this.book.getPageCount(), 1, this.book.getPageCount(), 1));
JPanel lastPageSpinnerPanel = new JPanel(new BorderLayout());
lastPageSpinnerPanel.add(new JLabel("Last Page:"), BorderLayout.WEST);
lastPageSpinnerPanel.add(this.lastPageSpinner, BorderLayout.CENTER);
this.autoPasteDelaySpinner = new JSpinner(new SpinnerNumberModel(0.2, 0.1, 5.0, 0.1)); this.autoPasteDelaySpinner = new JSpinner(new SpinnerNumberModel(0.2, 0.1, 5.0, 0.1));
JPanel autoPasteDelaySpinnerPanel = new JPanel(new BorderLayout());
autoPasteDelaySpinnerPanel.add(new JLabel("Auto-Paste Delay (s):"), BorderLayout.WEST); JComponent[] fields = {autoCheckbox, firstPageSpinner, lastPageSpinner, autoPasteDelaySpinner};
autoPasteDelaySpinnerPanel.add(this.autoPasteDelaySpinner, BorderLayout.CENTER); GridBagConstraints c = new GridBagConstraints();
setupPanel.add(this.autoCheckbox); c.gridx = 0;
setupPanel.add(firstPageSpinnerPanel); c.gridy = 0;
setupPanel.add(lastPageSpinnerPanel); c.anchor = GridBagConstraints.LINE_START;
setupPanel.add(autoPasteDelaySpinnerPanel); c.weightx = 0.5;
c.fill = GridBagConstraints.NONE;
c.insets = new Insets(5, 5, 5, 5);
for (String label : labels) {
setupPanel.add(new JLabel(label), c);
c.gridy++;
}
c.gridx = 1;
c.gridy = 0;
c.fill = GridBagConstraints.HORIZONTAL;
for (JComponent field : fields) {
setupPanel.add(field, c);
c.gridy++;
}
this.exportStatusPanel = new ExportStatusPanel(); this.exportStatusPanel = new ExportStatusPanel();

View File

@ -1,4 +1,4 @@
version=1.2.0 version=1.3.0
# Settings for the application's GUI. # Settings for the application's GUI.
frame.title=Block Book Binder frame.title=Block Book Binder
@ -19,5 +19,5 @@ about_dialog.source=html/about.html
book.max_pages=100 book.max_pages=100
book.page_max_lines=14 book.page_max_lines=14
book.page_max_width=113 book.page_max_width=113
book.page_max_chars=255 book.page_max_chars=700
book.default_char_width=5 book.default_char_width=5