diff --git a/core/src/main/java/com/volmit/iris/core/gui/IslandOverlay.java b/core/src/main/java/com/volmit/iris/core/gui/IslandOverlay.java new file mode 100644 index 000000000..732346ea1 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/gui/IslandOverlay.java @@ -0,0 +1,105 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +package com.volmit.iris.core.gui; + +import javax.swing.JComponent; +import javax.swing.JLayeredPane; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/** + * Lays an island over content that fills the whole area behind it. Dragging the island's right edge resizes it, + * always leaving the content some room beside it. + */ +final class IslandOverlay extends JLayeredPane { + private static final long serialVersionUID = -4182209135360718342L; + + private final IslandPanel island; + private final JComponent background; + private final int minimumIslandWidth; + private final int minimumUncoveredWidth; + private int islandWidth; + + IslandOverlay(IslandPanel island, JComponent background, int islandWidth, int minimumIslandWidth, int minimumUncoveredWidth) { + this.island = island; + this.background = background; + this.islandWidth = islandWidth; + this.minimumIslandWidth = minimumIslandWidth; + this.minimumUncoveredWidth = minimumUncoveredWidth; + add(background, JLayeredPane.DEFAULT_LAYER); + add(island, JLayeredPane.PALETTE_LAYER); + installResizing(); + } + + @Override + public void doLayout() { + background.setBounds(0, 0, getWidth(), getHeight()); + island.setBounds(0, 0, clampedIslandWidth(), getHeight()); + } + + private int clampedIslandWidth() { + int widest = Math.max(minimumIslandWidth, getWidth() - minimumUncoveredWidth); + return Math.clamp(islandWidth, minimumIslandWidth, widest); + } + + private void installResizing() { + MouseAdapter resizer = new MouseAdapter() { + private boolean dragging; + + @Override + public void mouseMoved(MouseEvent event) { + island.setCursor(island.isOnResizeEdge(event.getX(), event.getY()) + ? Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR) + : Cursor.getDefaultCursor()); + } + + @Override + public void mouseExited(MouseEvent event) { + if (!dragging) { + island.setCursor(Cursor.getDefaultCursor()); + } + } + + @Override + public void mousePressed(MouseEvent event) { + dragging = island.isOnResizeEdge(event.getX(), event.getY()); + } + + @Override + public void mouseDragged(MouseEvent event) { + if (!dragging) { + return; + } + islandWidth = event.getX(); + islandWidth = clampedIslandWidth(); + revalidate(); + repaint(); + } + + @Override + public void mouseReleased(MouseEvent event) { + dragging = false; + } + }; + island.addMouseListener(resizer); + island.addMouseMotionListener(resizer); + } +} diff --git a/core/src/main/java/com/volmit/iris/core/gui/IslandPanel.java b/core/src/main/java/com/volmit/iris/core/gui/IslandPanel.java index bd45b588b..9230cc771 100644 --- a/core/src/main/java/com/volmit/iris/core/gui/IslandPanel.java +++ b/core/src/main/java/com/volmit/iris/core/gui/IslandPanel.java @@ -28,8 +28,9 @@ import java.awt.RenderingHints; import java.awt.geom.RoundRectangle2D; /** - * A rounded panel set apart from the window edges, like the tool windows of IntelliJ's islands theme. Its right edge - * meets a split pane divider, which provides the gap there. + * A rounded panel floating over the window content with a gap all around it, like the tool windows of IntelliJ's + * islands theme. The gap belongs to the content behind it, except for the strip along the right edge, which grabs the + * island to resize it. */ final class IslandPanel extends JPanel { private static final long serialVersionUID = 7313084027614358519L; @@ -40,21 +41,37 @@ final class IslandPanel extends JPanel { IslandPanel(JComponent content) { super(new BorderLayout()); setOpaque(false); - setBorder(StudioTheme.emptyBorder(GAP + INNER_PADDING, GAP + INNER_PADDING, GAP + INNER_PADDING, INNER_PADDING)); + int inset = GAP + INNER_PADDING; + setBorder(StudioTheme.emptyBorder(inset, inset, inset, inset)); add(content, BorderLayout.CENTER); } + boolean isOnResizeEdge(int x, int y) { + int edgeWidth = StudioTheme.scale(GAP + INNER_PADDING); + return x >= getWidth() - edgeWidth && x < getWidth() && y >= 0 && y < getHeight(); + } + + // Clicks in the gap reach the content behind the island instead of the island itself. + @Override + public boolean contains(int x, int y) { + return island().contains(x, y) || isOnResizeEdge(x, y); + } + @Override protected void paintComponent(Graphics graphics) { Graphics2D canvas = (Graphics2D) graphics.create(); try { canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); canvas.setColor(StudioTheme.PANEL_BACKGROUND); - int gap = StudioTheme.scale(GAP); - int arc = StudioTheme.scale(CORNER_ARC); - canvas.fill(new RoundRectangle2D.Double(gap, gap, getWidth() - gap, getHeight() - gap * 2D, arc, arc)); + canvas.fill(island()); } finally { canvas.dispose(); } } + + private RoundRectangle2D island() { + double gap = StudioTheme.scale(GAP); + double arc = StudioTheme.scale(CORNER_ARC); + return new RoundRectangle2D.Double(gap, gap, getWidth() - gap * 2D, getHeight() - gap * 2D, arc, arc); + } } diff --git a/core/src/main/java/com/volmit/iris/core/gui/MapWindow.java b/core/src/main/java/com/volmit/iris/core/gui/MapWindow.java index bd0f01c83..cd3f2cf03 100644 --- a/core/src/main/java/com/volmit/iris/core/gui/MapWindow.java +++ b/core/src/main/java/com/volmit/iris/core/gui/MapWindow.java @@ -55,7 +55,6 @@ public final class MapWindow implements MapCanvas.Listener { private static final int SIDEBAR_WIDTH = 270; private static final int SIDEBAR_MINIMUM_WIDTH = 200; private static final int CONTENT_MINIMUM_WIDTH = 320; - private static final int SIDEBAR_DIVIDER_WIDTH = 6; private static final double PALETTE_RECENTER_DISTANCE_BLOCKS = 1D; private static final double VIEW_LIST_SHARE_WITH_PALETTES = 0.34D; @@ -117,7 +116,8 @@ public final class MapWindow implements MapCanvas.Listener { frame.setLayout(new BorderLayout()); frame.add(buildToolbar(), BorderLayout.NORTH); - frame.add(buildSidebarSplit(), BorderLayout.CENTER); + frame.add(new IslandOverlay(new IslandPanel(sidebar), content, StudioTheme.scale(SIDEBAR_WIDTH), + StudioTheme.scale(SIDEBAR_MINIMUM_WIDTH), StudioTheme.scale(CONTENT_MINIMUM_WIDTH)), BorderLayout.CENTER); frame.add(inspectorSlot, BorderLayout.EAST); frame.add(statusBars, BorderLayout.SOUTH); installShortcuts(frame.getRootPane()); @@ -226,20 +226,6 @@ public final class MapWindow implements MapCanvas.Listener { } } - // Dragging the gap beside the sidebar island makes it wider, so long palette names fit. - private JSplitPane buildSidebarSplit() { - IslandPanel island = new IslandPanel(sidebar); - island.setMinimumSize(StudioTheme.scaledSize(SIDEBAR_MINIMUM_WIDTH, 0)); - content.setMinimumSize(StudioTheme.scaledSize(CONTENT_MINIMUM_WIDTH, 0)); - JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, island, content); - split.setBorder(BorderFactory.createEmptyBorder()); - split.setBackground(StudioTheme.EDITOR_BACKGROUND); - split.setDividerSize(StudioTheme.scale(SIDEBAR_DIVIDER_WIDTH)); - split.setResizeWeight(0D); - split.setDividerLocation(StudioTheme.scale(SIDEBAR_WIDTH)); - return split; - } - // The palettes need the room more than the view list, which scrolls once it is squeezed. private int viewListHeightBesidePalettes() { int fullHeight = viewList.getPreferredSize().height + 4;