feat(io): GUI quest-box under the map
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
|||||||
|
package thb.jeanluc.adventure.io;
|
||||||
|
|
||||||
|
import thb.jeanluc.adventure.io.text.QuestText;
|
||||||
|
import thb.jeanluc.adventure.io.text.QuestView;
|
||||||
|
import thb.jeanluc.adventure.io.text.Span;
|
||||||
|
import thb.jeanluc.adventure.io.text.Style;
|
||||||
|
|
||||||
|
import javax.swing.JTextPane;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
|
import javax.swing.text.SimpleAttributeSet;
|
||||||
|
import javax.swing.text.StyleConstants;
|
||||||
|
import javax.swing.text.StyledDocument;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.Insets;
|
||||||
|
|
||||||
|
/** Read-only styled view of the quest log, shown under the map. */
|
||||||
|
public class QuestPanel extends JTextPane {
|
||||||
|
|
||||||
|
public QuestPanel(Font font) {
|
||||||
|
setEditable(false);
|
||||||
|
setBackground(new Color(0x0b, 0x0e, 0x13));
|
||||||
|
setForeground(new Color(0xcf, 0xd6, 0xe0));
|
||||||
|
setFont(font.deriveFont(12f));
|
||||||
|
setMargin(new Insets(8, 10, 8, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(QuestView view) {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
StyledDocument doc = getStyledDocument();
|
||||||
|
try {
|
||||||
|
doc.remove(0, doc.getLength());
|
||||||
|
for (Span sp : QuestText.render(view).spans()) {
|
||||||
|
SimpleAttributeSet as = new SimpleAttributeSet();
|
||||||
|
StyleConstants.setForeground(as, colorFor(sp.style()));
|
||||||
|
if (sp.style() == Style.HEADING) {
|
||||||
|
StyleConstants.setBold(as, true);
|
||||||
|
}
|
||||||
|
doc.insertString(doc.getLength(), sp.text(), as);
|
||||||
|
}
|
||||||
|
setCaretPosition(0);
|
||||||
|
} catch (BadLocationException ignored) {
|
||||||
|
// replace-only; cannot occur with getLength()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color colorFor(Style s) {
|
||||||
|
return switch (s) {
|
||||||
|
case HEADING -> new Color(0xc9, 0x8a, 0xe0);
|
||||||
|
case ITEM -> new Color(0x46, 0xc8, 0xd8);
|
||||||
|
case NPC -> new Color(0xe6, 0xc3, 0x4a);
|
||||||
|
case EXIT -> new Color(0x6f, 0xcf, 0x73);
|
||||||
|
case DANGER -> new Color(0xe0, 0x6c, 0x6c);
|
||||||
|
case DIM -> new Color(0x6b, 0x72, 0x80);
|
||||||
|
case PLAIN -> new Color(0xcf, 0xd6, 0xe0);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package thb.jeanluc.adventure.io;
|
|||||||
|
|
||||||
import thb.jeanluc.adventure.io.text.Hud;
|
import thb.jeanluc.adventure.io.text.Hud;
|
||||||
import thb.jeanluc.adventure.io.text.MapView;
|
import thb.jeanluc.adventure.io.text.MapView;
|
||||||
|
import thb.jeanluc.adventure.io.text.QuestView;
|
||||||
import thb.jeanluc.adventure.io.text.RoomView;
|
import thb.jeanluc.adventure.io.text.RoomView;
|
||||||
import thb.jeanluc.adventure.io.text.Span;
|
import thb.jeanluc.adventure.io.text.Span;
|
||||||
import thb.jeanluc.adventure.io.text.Style;
|
import thb.jeanluc.adventure.io.text.Style;
|
||||||
@@ -12,6 +13,7 @@ import javax.swing.BorderFactory;
|
|||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
import javax.swing.JTextPane;
|
import javax.swing.JTextPane;
|
||||||
@@ -70,6 +72,9 @@ public class SwingIO implements GameIO {
|
|||||||
private final JLabel hud;
|
private final JLabel hud;
|
||||||
private final MapPanel map;
|
private final MapPanel map;
|
||||||
private final JScrollPane sideScroll;
|
private final JScrollPane sideScroll;
|
||||||
|
private final QuestPanel quests;
|
||||||
|
private final JScrollPane questScroll;
|
||||||
|
private final JPanel sidePanel;
|
||||||
private final Font baseFont;
|
private final Font baseFont;
|
||||||
|
|
||||||
/** Detected display scale (≥ 1.0); HiDPI panels report > 1.0 where supported. */
|
/** Detected display scale (≥ 1.0); HiDPI panels report > 1.0 where supported. */
|
||||||
@@ -99,6 +104,14 @@ public class SwingIO implements GameIO {
|
|||||||
sideScroll = new JScrollPane(map);
|
sideScroll = new JScrollPane(map);
|
||||||
sideScroll.getViewport().setBackground(new Color(0x0e, 0x12, 0x18));
|
sideScroll.getViewport().setBackground(new Color(0x0e, 0x12, 0x18));
|
||||||
|
|
||||||
|
quests = new QuestPanel(baseFont);
|
||||||
|
questScroll = new JScrollPane(quests);
|
||||||
|
questScroll.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, new Color(0x2a, 0x2f, 0x3a)));
|
||||||
|
|
||||||
|
sidePanel = new JPanel(new BorderLayout());
|
||||||
|
sidePanel.add(sideScroll, BorderLayout.CENTER);
|
||||||
|
sidePanel.add(questScroll, BorderLayout.SOUTH);
|
||||||
|
|
||||||
input = new JTextField();
|
input = new JTextField();
|
||||||
input.addActionListener(e -> {
|
input.addActionListener(e -> {
|
||||||
String line = input.getText();
|
String line = input.getText();
|
||||||
@@ -111,7 +124,7 @@ public class SwingIO implements GameIO {
|
|||||||
frame.setLayout(new BorderLayout());
|
frame.setLayout(new BorderLayout());
|
||||||
frame.add(hud, BorderLayout.NORTH);
|
frame.add(hud, BorderLayout.NORTH);
|
||||||
frame.add(new JScrollPane(output), BorderLayout.CENTER);
|
frame.add(new JScrollPane(output), BorderLayout.CENTER);
|
||||||
frame.add(sideScroll, BorderLayout.EAST);
|
frame.add(sidePanel, BorderLayout.EAST);
|
||||||
frame.add(input, BorderLayout.SOUTH);
|
frame.add(input, BorderLayout.SOUTH);
|
||||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
frame.setSize((int) (900 * uiScale), (int) (600 * uiScale));
|
frame.setSize((int) (900 * uiScale), (int) (600 * uiScale));
|
||||||
@@ -171,8 +184,9 @@ public class SwingIO implements GameIO {
|
|||||||
int min = (int) (SIDE_MIN * uiScale);
|
int min = (int) (SIDE_MIN * uiScale);
|
||||||
int max = (int) (SIDE_MAX * uiScale);
|
int max = (int) (SIDE_MAX * uiScale);
|
||||||
int target = Math.max(min, Math.min(max, (int) (frame.getWidth() * SIDE_RATIO)));
|
int target = Math.max(min, Math.min(max, (int) (frame.getWidth() * SIDE_RATIO)));
|
||||||
sideScroll.setPreferredSize(new Dimension(target, 0));
|
sidePanel.setPreferredSize(new Dimension(target, 0));
|
||||||
sideScroll.revalidate();
|
questScroll.setPreferredSize(new Dimension(target, (int) (frame.getHeight() * 0.32)));
|
||||||
|
sidePanel.revalidate();
|
||||||
frame.validate();
|
frame.validate();
|
||||||
// The side panel changed size: let the map re-fit into it.
|
// The side panel changed size: let the map re-fit into it.
|
||||||
map.revalidate();
|
map.revalidate();
|
||||||
@@ -197,6 +211,7 @@ public class SwingIO implements GameIO {
|
|||||||
output.setFont(baseFont.deriveFont(main));
|
output.setFont(baseFont.deriveFont(main));
|
||||||
input.setFont(baseFont.deriveFont(main));
|
input.setFont(baseFont.deriveFont(main));
|
||||||
hud.setFont(baseFont.deriveFont(small));
|
hud.setFont(baseFont.deriveFont(small));
|
||||||
|
quests.setFont(baseFont.deriveFont(small));
|
||||||
map.setZoom(zoom);
|
map.setZoom(zoom);
|
||||||
frame.revalidate();
|
frame.revalidate();
|
||||||
frame.repaint();
|
frame.repaint();
|
||||||
@@ -312,6 +327,16 @@ public class SwingIO implements GameIO {
|
|||||||
map.show(view);
|
map.show(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setQuests(QuestView view) {
|
||||||
|
quests.show(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showQuests(QuestView view) {
|
||||||
|
// The GUI quest box is always visible; nothing extra to do.
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showMap(MapView view) {
|
public void showMap(MapView view) {
|
||||||
// The GUI map panel is always visible; nothing extra to do on the 'map' command.
|
// The GUI map panel is always visible; nothing extra to do on the 'map' command.
|
||||||
|
|||||||
Reference in New Issue
Block a user