JScratch
Loading...
Searching...
No Matches
MapMakerPanel.java
Go to the documentation of this file.
1package com.jscratch.ui;
2
3import com.jscratch.Project.MapMakerData;
4import com.jscratch.Project.LayerData;
5import com.jscratch.WorkspaceManager;
6import javax.imageio.ImageIO;
7import javax.swing.*;
8import java.awt.*;
9import java.awt.event.*;
10import java.awt.image.BufferedImage;
11import java.io.File;
12import java.io.IOException;
13import java.util.HashMap;
14import java.util.Map;
15
16public class MapMakerPanel extends JPanel {
18 private int selectedTile = 1;
19 private int currentLayerIndex = 1; // Default to "Main"
20 private int hoveredX = -1, hoveredY = -1;
21
22 private JPanel gridCanvas;
23 private JPanel sidebar;
24 private Map<Integer, Image> tileImages = new HashMap<>();
25
27 this.mapData = data;
28 setLayout(new BorderLayout());
29
30 sidebar = new JPanel();
31 sidebar.setLayout(new BoxLayout(sidebar, BoxLayout.Y_AXIS));
32 sidebar.setPreferredSize(new Dimension(200, 0));
33 sidebar.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY));
34
37
38 gridCanvas = new JPanel() {
39 @Override
40 protected void paintComponent(Graphics g) {
41 super.paintComponent(g);
42 drawGrid(g);
43 }
44 };
45 gridCanvas.setBackground(new Color(40, 44, 52));
47
48 MouseAdapter ma = new MouseAdapter() {
49 @Override public void mousePressed(MouseEvent e) { paintTile(e); }
50 @Override public void mouseDragged(MouseEvent e) { paintTile(e); }
51 @Override public void mouseMoved(MouseEvent e) {
52 int ds = mapData.tileSize * mapData.displayScale;
53 hoveredX = e.getX() / ds;
54 hoveredY = e.getY() / ds;
55 gridCanvas.repaint();
56 }
57 @Override public void mouseExited(MouseEvent e) {
58 hoveredX = -1; hoveredY = -1;
59 gridCanvas.repaint();
60 }
61 };
62 gridCanvas.addMouseListener(ma);
63 gridCanvas.addMouseMotionListener(ma);
64
65 add(sidebar, BorderLayout.WEST);
66 add(new JScrollPane(gridCanvas), BorderLayout.CENTER);
67 }
68
69 private void updateGridCanvasSize() {
70 int ds = mapData.tileSize * mapData.displayScale;
71 gridCanvas.setPreferredSize(new Dimension(mapData.width * ds, mapData.height * ds));
72 gridCanvas.revalidate();
73 }
74
75 private void setupSidebar() {
76 sidebar.removeAll();
77 sidebar.setLayout(new GridBagLayout());
78 GridBagConstraints gbc = new GridBagConstraints();
79 gbc.fill = GridBagConstraints.HORIZONTAL;
80 gbc.weightx = 1.0;
81 gbc.insets = new Insets(2, 5, 2, 5);
82 gbc.gridx = 0;
83 int gy = 0;
84
85 gbc.gridy = gy++;
86 sidebar.add(new JLabel("MAP SETTINGS"), gbc);
87
88 // Tile Size
89 gbc.gridy = gy++;
90 JPanel tsPanel = new JPanel(new BorderLayout());
91 tsPanel.add(new JLabel("Tile Size: "), BorderLayout.WEST);
92 JSpinner tsSpinner = new JSpinner(new SpinnerNumberModel(mapData.tileSize, 1, 128, 1));
93 tsSpinner.addChangeListener(e -> {
94 mapData.tileSize = (int) tsSpinner.getValue();
96 repaint();
97 });
98 tsPanel.add(tsSpinner, BorderLayout.CENTER);
99 sidebar.add(tsPanel, gbc);
100
101 // Grid Width
102 gbc.gridy = gy++;
103 JPanel wPanel = new JPanel(new BorderLayout());
104 wPanel.add(new JLabel("Width: "), BorderLayout.WEST);
105 JSpinner wSpinner = new JSpinner(new SpinnerNumberModel(mapData.width, 1, 500, 1));
106 wSpinner.addChangeListener(e -> {
107 int newW = (int) wSpinner.getValue();
108 resizeGrid(newW, mapData.height);
110 repaint();
111 });
112 wPanel.add(wSpinner, BorderLayout.CENTER);
113 sidebar.add(wPanel, gbc);
114
115 // Grid Height
116 gbc.gridy = gy++;
117 JPanel hPanel = new JPanel(new BorderLayout());
118 hPanel.add(new JLabel("Height: "), BorderLayout.WEST);
119 JSpinner hSpinner = new JSpinner(new SpinnerNumberModel(mapData.height, 1, 500, 1));
120 hSpinner.addChangeListener(e -> {
121 int newH = (int) hSpinner.getValue();
122 resizeGrid(mapData.width, newH);
124 repaint();
125 });
126 hPanel.add(hSpinner, BorderLayout.CENTER);
127 sidebar.add(hPanel, gbc);
128
129 // Display Scale
130 gbc.gridy = gy++;
131 JPanel sPanel = new JPanel(new BorderLayout());
132 sPanel.add(new JLabel("View Scale: "), BorderLayout.WEST);
133 JSpinner sSpinner = new JSpinner(new SpinnerNumberModel(mapData.displayScale, 1, 20, 1));
134 sSpinner.addChangeListener(e -> {
135 mapData.displayScale = (int) sSpinner.getValue();
137 repaint();
138 });
139 sPanel.add(sSpinner, BorderLayout.CENTER);
140 sidebar.add(sPanel, gbc);
141
142 gbc.gridy = gy++;
143 sidebar.add(new JSeparator(), gbc);
144
145 gbc.gridy = gy++;
146 sidebar.add(new JLabel("LAYERS"), gbc);
147 ButtonGroup bg = new ButtonGroup();
148 for (int i = 0; i < mapData.layers.size(); i++) {
149 final int idx = i;
150 JRadioButton rb = new JRadioButton(mapData.layers.get(i).name, i == currentLayerIndex);
151 rb.addActionListener(e -> currentLayerIndex = idx);
152 bg.add(rb);
153 gbc.gridy = gy++;
154 sidebar.add(rb, gbc);
155 }
156
157 gbc.gridy = gy++;
158 sidebar.add(new JSeparator(), gbc);
159 gbc.gridy = gy++;
160 sidebar.add(new JLabel("TILES"), gbc);
161
162 JPanel palette = new JPanel(new GridLayout(0, 2, 5, 5));
163 for (int i = 1; i <= 20; i++) {
164 if (!mapData.tileAssets.containsKey(i) && i > 1 && !mapData.tileAssets.containsKey(i-1)) break;
165 final int tileId = i;
166 JButton b = new JButton() {
167 @Override protected void paintComponent(Graphics g) {
168 super.paintComponent(g);
169 Image img = tileImages.get(tileId);
170 if (img != null) g.drawImage(img, 5, 5, getWidth()-10, getHeight()-10, null);
171 else {
172 g.setColor(Color.LIGHT_GRAY);
173 g.fillRect(5, 5, getWidth()-10, getHeight()-10);
174 g.setColor(Color.BLACK);
175 g.drawString(""+tileId, 10, 20);
176 }
177 if (selectedTile == tileId) {
178 g.setColor(Color.YELLOW);
179 g.drawRect(2, 2, getWidth()-4, getHeight()-4);
180 }
181 }
182 };
183 b.setPreferredSize(new Dimension(40, 40));
184 b.addActionListener(e -> {
185 selectedTile = tileId;
186 if (!mapData.tileAssets.containsKey(tileId)) configureTile(tileId);
187 repaint();
188 });
189 palette.add(b);
190 }
191 gbc.gridy = gy++;
192 sidebar.add(palette, gbc);
193
194 gbc.gridy = gy++;
195 JButton configBtn = new JButton("Config Selected Tile");
196 configBtn.addActionListener(e -> configureTile(selectedTile));
197 sidebar.add(configBtn, gbc);
198
199 gbc.gridy = gy++;
200 JButton exportBtn = new JButton("Export PNG");
201 exportBtn.addActionListener(e -> exportToPNG());
202 sidebar.add(exportBtn, gbc);
203
204 // Filler to push everything up
205 gbc.gridy = gy++;
206 gbc.weighty = 1.0;
207 sidebar.add(Box.createGlue(), gbc);
208
209 sidebar.revalidate();
210 }
211
212 private void resizeGrid(int newW, int newH) {
213 if (newW == mapData.width && newH == mapData.height) return;
214 for (LayerData layer : mapData.layers) {
215 int[][] newTiles = new int[newW][newH];
216 for (int x = 0; x < Math.min(newW, mapData.width); x++) {
217 for (int y = 0; y < Math.min(newH, mapData.height); y++) {
218 newTiles[x][y] = layer.tiles[x][y];
219 }
220 }
221 layer.tiles = newTiles;
222 }
223 mapData.width = newW;
224 mapData.height = newH;
225 }
226
227 private void paintTile(MouseEvent e) {
228 int ds = mapData.tileSize * mapData.displayScale;
229 int tx = e.getX() / ds;
230 int ty = e.getY() / ds;
231 if (tx >= 0 && tx < mapData.width && ty >= 0 && ty < mapData.height) {
232 LayerData layer = mapData.layers.get(currentLayerIndex);
233 layer.tiles[tx][ty] = SwingUtilities.isLeftMouseButton(e) ? selectedTile : 0;
234 gridCanvas.repaint();
235 }
236 }
237
238 private void drawGrid(Graphics g, boolean showGridLines) {
239 if (mapData == null) return;
240 int ds = mapData.tileSize * (showGridLines ? mapData.displayScale : 1);
241
242 for (int i = 0; i < mapData.layers.size(); i++) {
243 LayerData layer = mapData.layers.get(i);
244 float alpha = (i == currentLayerIndex || !showGridLines) ? 1.0f : 0.4f;
245 Graphics2D g2d = (Graphics2D) g.create();
246 g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
247
248 for (int x = 0; x < mapData.width; x++) {
249 for (int y = 0; y < mapData.height; y++) {
250 int tileId = layer.tiles[x][y];
251 if (tileId > 0) {
252 Image img = tileImages.get(tileId);
253 if (img != null) {
254 g2d.drawImage(img, x * ds, y * ds, ds, ds, null);
255 } else {
256 g2d.setColor(getFallbackColor(tileId));
257 g2d.fillRect(x * ds, y * ds, ds, ds);
258 }
259 }
260 }
261 }
262 g2d.dispose();
263 }
264
265 if (showGridLines) {
266 g.setColor(new Color(60, 60, 60));
267 for (int x = 0; x <= mapData.width; x++) g.drawLine(x * ds, 0, x * ds, mapData.height * ds);
268 for (int y = 0; y <= mapData.height; y++) g.drawLine(0, y * ds, mapData.width * ds, y * ds);
269
271 g.setColor(new Color(255, 255, 255, 80));
272 g.fillRect(hoveredX * ds, hoveredY * ds, ds, ds);
273 }
274 }
275 }
276
277 private void exportToPNG() {
278 BufferedImage img = new BufferedImage(mapData.width * mapData.tileSize, mapData.height * mapData.tileSize, BufferedImage.TYPE_INT_ARGB);
279 Graphics2D g2d = img.createGraphics();
280 drawGrid(g2d, false);
281 g2d.dispose();
282
283 JFileChooser saver = new JFileChooser(WorkspaceManager.getAssetsDir());
284 if (saver.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
285 File file = saver.getSelectedFile();
286 if (!file.getName().endsWith(".png")) file = new File(file.getAbsolutePath() + ".png");
287 try {
288 ImageIO.write(img, "PNG", file);
289 JOptionPane.showMessageDialog(this, "Exported successfully!");
290 } catch (IOException e) {
291 e.printStackTrace();
292 }
293 }
294 }
295
296
297 private void drawGrid(Graphics g) {
298 drawGrid(g, true);
299 }
300
301
302 private Color getFallbackColor(int id) {
303 return new Color((id * 50) % 255, (id * 100) % 255, (id * 150) % 255);
304 }
305
306 private static class JButtonGroup extends ButtonGroup {
307 @Override
308 public void add(AbstractButton b) {
309 super.add(b);
310 }
311 }
312
313 public void setMapData(MapMakerData data) {
314 this.mapData = data;
316 setupSidebar();
317 repaint();
318 }
319 private void loadTileImages() {
320 tileImages.clear();
321 for (Map.Entry<Integer, String> entry : mapData.tileAssets.entrySet()) {
322 File assetFile = new File(WorkspaceManager.getAssetsDir(), entry.getValue());
323 if (assetFile.exists()) {
324 try {
325 tileImages.put(entry.getKey(), ImageIO.read(assetFile));
326 } catch (IOException e) {
327 e.printStackTrace();
328 }
329 }
330 }
331 }
332
333 private void configureTile(int id) {
334 File assetsDir = WorkspaceManager.getAssetsDir();
335 String[] files = assetsDir.list((dir, name) -> name.endsWith(".png") || name.endsWith(".jpg"));
336 if (files == null || files.length == 0) {
337 JOptionPane.showMessageDialog(this, "No assets found in assets folder.");
338 return;
339 }
340 String selected = (String) JOptionPane.showInputDialog(this, "Select asset for tile " + id, "Tile Config",
341 JOptionPane.QUESTION_MESSAGE, null, files, mapData.tileAssets.get(id));
342
343 if (selected != null) {
344 mapData.tileAssets.put(id, selected);
346 setupSidebar();
347 repaint();
348 }
349 }
350}
void drawGrid(Graphics g, boolean showGridLines)
void resizeGrid(int newW, int newH)
void setMapData(MapMakerData data)
MapMakerPanel(MapMakerData data)
Map< Integer, Image > tileImages