Skip to content

Commit

Permalink
Adjusted how scrolling and zooming works in the designer (#2566)
Browse files Browse the repository at this point in the history
  • Loading branch information
breiler committed Jul 4, 2024
1 parent 131327f commit 72f96f1
Show file tree
Hide file tree
Showing 9 changed files with 326 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import com.willwinder.ugs.nbp.designer.actions.UndoManager;
import com.willwinder.ugs.nbp.designer.entities.selection.SelectionManager;
import com.willwinder.ugs.nbp.designer.gui.DrawingContainer;
import com.willwinder.ugs.nbp.designer.gui.MainMenu;
import com.willwinder.ugs.nbp.designer.gui.DrawingScrollContainer;
import com.willwinder.ugs.nbp.designer.gui.DrawingOverlayContainer;
import com.willwinder.ugs.nbp.designer.gui.PopupMenuFactory;
import com.willwinder.ugs.nbp.designer.gui.ToolBox;
import com.willwinder.ugs.nbp.designer.gui.selectionsettings.SelectionSettingsPanel;
Expand Down Expand Up @@ -53,13 +54,14 @@ public DesignerMain() {
SelectionManager selectionManager = ControllerFactory.getSelectionManager();
CentralLookup.getDefault().add(selectionManager);

DrawingContainer drawingContainer = new DrawingContainer(controller);
DrawingScrollContainer drawingContainer = new DrawingScrollContainer(controller);
selectionManager.addSelectionListener(e -> drawingContainer.repaint());

JSplitPane toolsSplit = createRightPanel(controller);
DrawingOverlayContainer overlayToolContainer = new DrawingOverlayContainer(controller, drawingContainer);

JSplitPane toolsSplit = createRightPanel(controller);
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
drawingContainer, toolsSplit);
overlayToolContainer, toolsSplit);
splitPane.setResizeWeight(0.95);

getContentPane().add(splitPane, BorderLayout.CENTER);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void addChild(Entity entity, int index) {
invalidateBounds();
}

private void invalidateBounds() {
public void invalidateBounds() {
cachedBounds = null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ public void render(Graphics2D graphics, Drawing drawing) {
}

// Draw the bounds
double margin = ResizeControl.MARGIN / drawing.getScale();
graphics.setColor(Colors.CONTROL_BORDER);
graphics.setStroke(new BasicStroke(Double.valueOf(0.8 / drawing.getScale()).floatValue()));
graphics.setStroke(new BasicStroke((float) (0.8f / drawing.getScale())));
Rectangle2D bounds = getRelativeShape().getBounds2D();
bounds.setFrame(bounds.getX() - margin, bounds.getY() - margin, bounds.getWidth() + (margin * 2), bounds.getHeight() + (margin * 2));
bounds.setFrame(bounds.getX() , bounds.getY() , bounds.getWidth(), bounds.getHeight());
graphics.draw(getSelectionManager().getTransform().createTransformedShape(bounds));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2021 Will Winder
Copyright 2021-2024 Will Winder
This file is part of Universal Gcode Sender (UGS).
Expand Down Expand Up @@ -54,6 +54,7 @@ This file is part of Universal Gcode Sender (UGS).
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.Serial;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
Expand All @@ -65,20 +66,20 @@ This file is part of Universal Gcode Sender (UGS).
public class Drawing extends JPanel {

public static final double MIN_SCALE = 0.05;
@Serial
private static final long serialVersionUID = 1298712398723987873L;
private static final int MARGIN = 100;
private final transient EntityGroup globalRoot;
private final transient EntityGroup entitiesRoot;
private final transient EntityGroup controlsRoot;
private final transient Set<DrawingListener> listeners = Sets.newConcurrentHashSet();
private final transient Throttler refreshThrottler;
private double scale;
private AffineTransform transform;
private long lastUpdate;
private final transient Rectangle2D currentBounds = new Rectangle(0, 0, 8, 8);
private double scale;
private Point2D.Double position = new Point2D.Double();
private Dimension oldMinimumSize;

public Drawing(Controller controller) {
refreshThrottler = new Throttler(this::refresh, 2000);
refreshThrottler = new Throttler(this::refresh, 1000);

globalRoot = new EntityGroup();
globalRoot.addChild(new GridControl(controller));
Expand Down Expand Up @@ -219,14 +220,14 @@ public void setScale(double scale) {
if (this.scale != newScale) {
this.scale = newScale;
notifyListeners(DrawingEvent.SCALE_CHANGED);
refresh();
}
}

@Override
public Dimension getMinimumSize() {
int margin = (int) (MARGIN * 2 * scale);
int width = (int) (currentBounds.getWidth() * scale) + margin;
int height = (int) (currentBounds.getHeight() * scale) + margin;
int width = (int) (currentBounds.getWidth() );
int height = (int) (currentBounds.getHeight());
return new Dimension(width, height);
}

Expand All @@ -235,11 +236,14 @@ public Dimension getPreferredSize() {
return getMinimumSize();
}

private void refresh() {
public void refresh() {
repaint();
globalRoot.invalidateBounds();
updateBounds();
Dimension minimumSize = getMinimumSize();
firePropertyChange("minimumSize", minimumSize.width, minimumSize.height);
firePropertyChange("preferredSize", minimumSize.width, minimumSize.height);
firePropertyChange("minimumSize", oldMinimumSize, minimumSize);
firePropertyChange("preferredSize", oldMinimumSize, minimumSize);
oldMinimumSize = minimumSize;
revalidate();
}

Expand All @@ -248,17 +252,10 @@ public void addListener(DrawingListener listener) {
}

public AffineTransform getTransform() {
// Don't update this every time or else it will be hard to move entites outside the canvas
if (System.currentTimeMillis() > lastUpdate + 50) {

transform = AffineTransform.getScaleInstance(1, -1);
transform.translate(0, -getHeight());
transform.scale(scale, scale);
transform.translate(MARGIN / 4d, MARGIN / 4d);
transform.translate(-getBounds().getMinX(), -getBounds().getMinY());
lastUpdate = System.currentTimeMillis();
}

AffineTransform transform = AffineTransform.getScaleInstance(1, -1);
transform.translate(0, -getHeight());
transform.scale(scale, scale);
transform.translate(-position.x, -position.y);
return transform;
}

Expand All @@ -277,15 +274,27 @@ public void clear() {
entitiesRoot.removeAll();
}


@Override
public Rectangle getBounds() {
updateBounds();
return currentBounds.getBounds();
}

private void updateBounds() {
Rectangle2D bounds = globalRoot.getBounds();
double minX = Math.min(currentBounds.getMinX(), bounds.getMinX());
double minY = Math.min(currentBounds.getMinY(), bounds.getMinY());
double maxX = Math.max(currentBounds.getMaxX(), bounds.getMaxX());
double maxY = Math.max(currentBounds.getMaxY(), bounds.getMaxY());
double minX = (bounds.getMinX()) * scale;
double minY = (bounds.getMinY()) * scale;
double maxX = (bounds.getMaxX()) * scale;
double maxY = (bounds.getMaxY()) * scale;
currentBounds.setRect(minX, minY, maxX - minX, maxY - minY);
return currentBounds.getBounds();
}

public void setPosition(double x, double y) {
position = new Point2D.Double(x / scale, y / scale);
refresh();
}

public Point2D.Double getPosition() {
return new Point2D.Double(position.x * scale, position.y * scale);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
Copyright 2024 Will Winder
This file is part of Universal Gcode Sender (UGS).
UGS 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.
UGS 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 UGS. If not, see <http://www.gnu.org/licenses/>.
*/
package com.willwinder.ugs.nbp.designer.gui;

import com.willwinder.ugs.nbp.designer.actions.OpenStockSettingsAction;
import com.willwinder.ugs.nbp.designer.actions.OpenToolSettingsAction;
import com.willwinder.ugs.nbp.designer.logic.Controller;

import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;

/**
* A container that will add button overlays
*
* @author Joacim Breiler
*/
public class DrawingOverlayContainer extends JPanel implements ComponentListener {
private final JPanel buttonPanel;
private final JComponent component;

public DrawingOverlayContainer(Controller controller, JComponent component) {
setLayout(new BorderLayout());

this.component = component;
buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
buttonPanel.setOpaque(false);

ToolButton toolButton = new ToolButton(controller);
toolButton.setMinimumSize(new Dimension(60, 40));
toolButton.setMaximumSize(new Dimension(100, 40));
toolButton.addActionListener(new OpenToolSettingsAction(controller));
buttonPanel.add(toolButton);

add(Box.createHorizontalStrut(6));
PanelButton stockButton = new PanelButton("Stock", controller.getSettings().getStockSizeDescription());
stockButton.setMinimumSize(new Dimension(60, 40));
stockButton.setMaximumSize(new Dimension(100, 40));
controller.getSettings().addListener(() -> stockButton.setText(controller.getSettings().getStockSizeDescription()));
stockButton.addActionListener(new OpenStockSettingsAction(controller));
buttonPanel.add(stockButton);

JLayeredPane layeredPane = new JLayeredPane();

layeredPane.add(component, JLayeredPane.DEFAULT_LAYER, 0);
layeredPane.add(buttonPanel, JLayeredPane.PALETTE_LAYER, 0);
add(layeredPane, BorderLayout.CENTER);
revalidate();

// This is a workaround to be able to resize the layered pane
addComponentListener(this);
}

@Override
public void componentResized(ComponentEvent e) {
// Needed to properly resize the layered pane
SwingUtilities.invokeLater(() -> {
Rectangle bounds = getBounds();
component.setBounds(0, 0, bounds.width, bounds.height);
buttonPanel.setBounds(0, bounds.height - 80, bounds.width - 30, 80);
revalidate();
});
}

@Override
public void componentMoved(ComponentEvent e) {
// Not used
}

@Override
public void componentShown(ComponentEvent e) {
// Not used
}

@Override
public void componentHidden(ComponentEvent e) {
// Not used
}
}
Loading

0 comments on commit 72f96f1

Please sign in to comment.