Reputation: 782
So, I am trying to click-and-drag a line around. I am almost there but I think I have a problem calculating where the line is in respect to where the user is dragging. When I click and drag, it does move, just not in the way I would think. I think I have a problem with this code:
@Override
public void mouseDragged(MouseEvent e) {
Point p = e.getPoint();
if (resizing) {
endPoint.x = p.x;
endPoint.y = p.y;
double offsetX = endPoint.x - startPoint.x;
double offsetY = endPoint.y - startPoint.y;
int newX1 = (int)(lines.get(currentIndex).getX1() - offsetX);
int newX2 = (int)(lines.get(currentIndex).getX2() - offsetX);
int newY1 = (int)(lines.get(currentIndex).getY1() - offsetY);
int newY2 = (int)(lines.get(currentIndex).getY2() - offsetY);
lines.get(currentIndex).setLine(newX1, newY1, newX2, newY2);
repaint();
}
}
Any idea on how I can get it to work?
MCVE
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Panel;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Editor {
public static void main(String[] args) {
new Editor();
}
public Editor() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager
.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException
| UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new UMLWindow2();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(30, 30, 1000, 700);
frame.getContentPane().setBackground(Color.white);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
});
}
public static class UMLWindow2 extends JFrame {
Shapes shapeList = new Shapes();
Panel panel;
private static final long serialVersionUID = 1L;
public UMLWindow2() {
addMenus();
panel = new Panel();
}
public void addMenus() {
getContentPane().add(shapeList);
setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
shapeList.addLine();
}
}
// Shapes class, used to draw the shapes on the panel
// as well as implements the MouseListener for dragging
public static class Shapes extends JPanel {
private static final long serialVersionUID = 1L;
private List<Line2D.Double> lines = new ArrayList<Line2D.Double>();
private Line2D.Double linePath;
double phi = Math.toRadians(40);
int barb = 20;
Boolean hovering = false;
Boolean resizing = false;
public Shapes() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
public void addLine() {
linePath = new Line2D.Double(300, 300, 500, 500);
lines.add(linePath);
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(2));
for (Line2D line : lines) {
g2.setColor(Color.BLACK);
Point sw = new Point((int) line.getX1(), (int) line.getY1());
Point ne = new Point((int) line.getX2(), (int) line.getY2());
g2.draw(line);
drawArrowHead(g2, ne, sw, Color.BLACK);
}
}
private void drawArrowHead(Graphics2D g2, Point tip, Point tail,
Color color) {
g2.setPaint(color);
double dy = tip.y - tail.y;
double dx = tip.x - tail.x;
double theta = Math.atan2(dy, dx);
double x, y, rho = theta + phi;
for (int j = 0; j < 2; j++) {
x = tip.x - barb * Math.cos(rho);
y = tip.y - barb * Math.sin(rho);
g2.draw(new Line2D.Double(tip.x, tip.y, x, y));
rho = theta - phi;
}
}
class MyMouseAdapter extends MouseAdapter {
int currentIndex;
Point2D.Double startPoint = new Point2D.Double();
Point2D.Double endPoint = new Point2D.Double();
@Override
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
if (hovering) {
System.out.println("Starting to resize");
startPoint.x = p.x;
startPoint.y = p.y;
resizing = true;
}
}
@Override
public void mouseDragged(MouseEvent e) {
Point p = e.getPoint();
if (resizing) {
endPoint.x = p.x;
endPoint.y = p.y;
double offsetX = endPoint.x - startPoint.x;
double offsetY = endPoint.y - startPoint.y;
System.out.println("Point 1: "
+ lines.get(currentIndex).getX1() + ", "
+ lines.get(currentIndex).getY1() + " Point 2: "
+ lines.get(currentIndex).getX2() + ", "
+ lines.get(currentIndex).getY2());
int newX1 = (int) (lines.get(currentIndex).getX1() + offsetX);
int newX2 = (int) (lines.get(currentIndex).getX2() + offsetX);
int newY1 = (int) (lines.get(currentIndex).getY1() + offsetY);
int newY2 = (int) (lines.get(currentIndex).getY2() + offsetY);
lines.get(currentIndex).setLine(newX1, newY1, newX2, newY2);
repaint();
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (resizing) {
System.out.println("Done resizing");
resizing = false;
}
}
@Override
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
Rectangle2D.Double rect = new Rectangle2D.Double(p.x - 1,
p.y - 1, 3, 3);
for (int i = 0; i < lines.size(); i++) {
Line2D line = lines.get(i);
if (line.intersects(rect)) {
setCursor(new Cursor(Cursor.HAND_CURSOR));
currentIndex = i;
hovering = true;
} else {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
hovering = false;
}
}
}
}
}
}
Upvotes: 0
Views: 972
Reputation: 324118
I don't know if this will help or confuse, but one approach for dragging is to create a custom class that is able to drag any component. The next problem is the ability to create a components of different shapes.
You can start by checking out:
Playing With Shapes. This class allows you to create a custom component of any Shape
. The actual bounds of the Shape (not the rectangular bounds) will be respected by the component so handling mouse events is easy.
Component Mover. This class allows you to drag a component simply by registering the component with the ComponentMover
.
The following example code attempts to demonstrate these two classes. Creating enclosed shapes (circles, squares, stars) is relatively easy. Creating your arrow head is more difficult. I tried it two different ways.
The first was to use a GeneralPath
. The code was easy, but the Shape.contains(...) method did not work as expected since the GeneralPath appears to close the start/end points to create a triangle. This is ok for painting, but not good for moving since clicking anywhere in the triangle will cause the arrow to move.
The second attempt was to create a more complex Shape by creating two different triangles of slightly different sizes and then subtracting the area of the smaller triangle from the larger to leave an outline of the arrow. In this case the Shape.contains(..) method works as expected so dragging also works as expected.
Have fun with the code:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.border.*;
public class ShapeComponentTest
{
private static void createAndShowUI()
{
Border line = new LineBorder(Color.BLUE, 5);
Polygon outer = new Polygon();
outer.addPoint(0, 50);
outer.addPoint(50, 25);
outer.addPoint(0, 0);
Polygon inner = new Polygon();
inner.addPoint(0, 45);
inner.addPoint(43, 25);
inner.addPoint(0, 5);
Area arrow = new Area(outer);
arrow.subtract(new Area(inner));
GeneralPath part1 = new GeneralPath();
part1.moveTo(0, 50);
part1.lineTo(50, 25);
part1.lineTo(0, 0);
ShapeComponent ball = new ShapeComponent( arrow ); // drags correctly
// OutlineComponent ball = new OutlineComponent( part1, 3 ); // drags incorrectly
ball.setForeground(Color.ORANGE);
ball.setBorder( line );
ball.setSize( ball.getPreferredSize() );
OutlineComponent square = new OutlineComponent( new Rectangle(30, 30) );
square.setForeground(Color.ORANGE);
square.setLocation(50, 50);
square.setBorder(line);
square.setSize( square.getPreferredSize() );
OutlineComponent star = new OutlineComponent( ShapeUtils.radiusShape(16, 20, 8) );
star.setForeground(Color.ORANGE);
star.setLocation(100, 100);
star.setBorder(line);
star.setSize( star.getPreferredSize() );
ComponentMover cm = new ComponentMover();
cm.registerComponent(star);
cm.registerComponent(ball);
cm.registerComponent(square);
JPanel panel = new JPanel();
panel.setLayout(null);
panel.add(ball);
panel.add(square);
panel.add(star);
JFrame frame = new JFrame(); frame.add(panel);
frame.setSize(200, 200); frame.setVisible(true);
MouseListener ml = new MouseAdapter()
{
public void mouseClicked( MouseEvent e )
{
System.out.println( "clicked " );
}
};
ball.addMouseListener( ml );
square.addMouseListener( ml );
star.addMouseListener( ml );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Upvotes: 2