commit 0ece74282dcbcd478ab4829a9f6d14206c55ad1f Author: Etienne VIENOT Date: Fri Mar 22 15:46:43 2019 +0100 Init diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..e461bea --- /dev/null +++ b/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/.project b/.project new file mode 100644 index 0000000..6a64d99 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + ExempleJPanelConversation + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..bb35fa0 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/src/ClassePrincipale.java b/src/ClassePrincipale.java new file mode 100644 index 0000000..4f305e4 --- /dev/null +++ b/src/ClassePrincipale.java @@ -0,0 +1,8 @@ +import packageIHM.MaJFrame; + +public class ClassePrincipale { + public static void main(String[] args) { + MaJFrame maFen = new MaJFrame(); + maFen.setVisible(true); + } +} diff --git a/src/packageIHM/JPanelConversation.java b/src/packageIHM/JPanelConversation.java new file mode 100644 index 0000000..543b203 --- /dev/null +++ b/src/packageIHM/JPanelConversation.java @@ -0,0 +1,158 @@ +package packageIHM; +import java.awt.Color; +import java.awt.Graphics; +import java.util.ArrayList; +import java.util.regex.Pattern; + +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.SwingConstants; + + +public class JPanelConversation extends JPanel +{ + private static final long serialVersionUID = 1L; + private ArrayList messages; + private TextBubble border; + private final int marginLeft = 0; + private final int marginTop = 0; + private final int thickness = 2; + private final int radius = 8; + private final int pointerSize = 10; + private int offset = 0; + private boolean initialized = false; + private int maxCharByLine = 50; + + + JPanelConversation(MaJFrame mjf, ArrayList messages) { + setLayout(null); + int posX = marginLeft; + int posY; + this.messages = messages; + JTextArea bubble = null; + + for (Message msg : messages) { + if (bubble!=null) { + posY = bubble.getLocation().y+bubble.getHeight(); + } else { + posY = marginTop; + } + bubble = new JTextArea(wrap(msg.getMessage(),maxCharByLine)); + bubble.setDisabledTextColor(Color.BLACK); + bubble.setEnabled(false); + border = new TextBubble(msg.getColor(),thickness,radius,pointerSize, msg.getSide()); + bubble.setBorder(border); + bubble.setOpaque(true); + bubble .setBackground(Color.WHITE); + bubble.setSize(bubble.getPreferredSize()); + bubble.setLocation(posX, posY); + bubble.setLineWrap(true); + bubble.addMouseMotionListener(new RedispatchingMouseAdapter() ); + add(bubble); + msg.setBubble(bubble); + msg.setPosY(posY); + } + } + + + private void createBubbleIfNecessary() { + int currentHeight = 0; + for (Message msg : messages) { + if (msg.getBubble()==null) { + JTextArea bubble = new JTextArea(wrap(msg.getMessage(),maxCharByLine)); + bubble.setDisabledTextColor(Color.BLACK); + bubble.setEnabled(false); + border = new TextBubble(msg.getColor(),thickness,radius,pointerSize, msg.getSide()); + bubble.setBorder(border); + bubble.setOpaque(true); + bubble.setBackground(Color.WHITE); + bubble.setSize(bubble.getPreferredSize()); + bubble.setLocation(marginLeft, currentHeight); + bubble.setLineWrap(true); + add(bubble); + msg.setBubble(bubble); + msg.setPosY(currentHeight); + calculateOffsetToShowLastMessage(); + } else { + currentHeight = currentHeight + msg.getBubble().getHeight(); + } + } + } + + + private int getFullHeightMessage() { + int fullHeight = 0; + for (Message msg : messages) { + fullHeight = fullHeight + msg.getBubble().getHeight(); + } + return fullHeight; + } + + + public void calculateOffsetToShowLastMessage() { + int fullHeight = getFullHeightMessage(); + if (fullHeight>getHeight()) { + offset = fullHeight-getHeight(); + } + } + + public void up(int x) { + offset = offset + x; + } + + public void down(int x) { + offset = offset - x; + } + + public void paintComponent(Graphics g) { + super.paintComponent(g); + if (!initialized) { + calculateOffsetToShowLastMessage(); + initialized=true; + } + createBubbleIfNecessary(); + for (Message msg : messages) { + if (msg.getSide()==SwingConstants.RIGHT) { + msg.getBubble().setLocation(getWidth()-msg.getBubble().getWidth(), msg.getPosY()-offset); + } else { + msg.getBubble().setLocation(msg.getBubble().getX(), msg.getPosY()-offset); + + } + } + } + + + private static final String linebreak = "\n"; // or "\r\n"; + + public static String wrap(String string, int lineLength) { + StringBuilder b = new StringBuilder(); + for (String line : string.split(Pattern.quote(linebreak))) { + b.append(wrapLine(line, lineLength)); + } + return b.toString(); + } + + private static String wrapLine(String line, int lineLength) { + if (line.length() == 0) return linebreak; + if (line.length() <= lineLength) return line + linebreak; + String[] words = line.split(" "); + StringBuilder allLines = new StringBuilder(); + StringBuilder trimmedLine = new StringBuilder(); + for (String word : words) { + if (trimmedLine.length() + 1 + word.length() <= lineLength) { + trimmedLine.append(word).append(" "); + } else { + allLines.append(trimmedLine).append(linebreak); + trimmedLine = new StringBuilder(); + trimmedLine.append(word).append(" "); + } + } + if (trimmedLine.length() > 0) { + allLines.append(trimmedLine); + } + allLines.append(linebreak); + return allLines.toString(); + } + +} + diff --git a/src/packageIHM/MaJFrame.java b/src/packageIHM/MaJFrame.java new file mode 100644 index 0000000..3b5529f --- /dev/null +++ b/src/packageIHM/MaJFrame.java @@ -0,0 +1,140 @@ +package packageIHM; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; +import java.util.ArrayList; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.SwingConstants; +import javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; + +public class MaJFrame extends JFrame implements MouseMotionListener{ + + private static final long serialVersionUID = 7252959164975426293L; + private JPanel contentPane; + private JPanelConversation conversationPanel; + private JPanel panel; + private JTextField textFieldMsg; + private ArrayList messages; + private int previousY; + + /** + * Create the frame. + */ + public MaJFrame() { + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setBounds(100, 100, 450, 300); + contentPane = new JPanel(); + contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); + contentPane.setLayout(new BorderLayout(0, 0)); + + panel = new JPanel(); + panel.setBorder(new LineBorder(new Color(0, 0, 0))); + contentPane.add(panel, BorderLayout.NORTH); + panel.setLayout(new BorderLayout(0, 0)); + + + Action action = new AbstractAction() { + private static final long serialVersionUID = 1L; + @Override + public void actionPerformed(ActionEvent e) { + addMessage(); + } + }; + + JButton buttonAdd = new JButton("Add"); + panel.add(buttonAdd, BorderLayout.EAST); + buttonAdd.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + addMessage(); + } + }); + + + textFieldMsg = new JTextField(); + panel.add(textFieldMsg, BorderLayout.CENTER); + textFieldMsg.setColumns(10); + textFieldMsg.addActionListener(action); + + messages = new ArrayList(); + String msgLeft = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n" + + "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."; + String msgRight = "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n" + + "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum"; + + for (int i=0;i<20;i++) { + if (i%2==0) { + messages.add(new Message(Color.BLUE, i + " " + msgLeft, SwingConstants.LEFT)); + } else { + messages.add(new Message(Color.RED, i + " " + msgRight, SwingConstants.RIGHT)); + } + } + conversationPanel = new JPanelConversation(this, messages); + conversationPanel .setBackground(Color.WHITE); + conversationPanel.addMouseMotionListener(this); + contentPane.add(conversationPanel, BorderLayout.CENTER); + setContentPane(contentPane); + + addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent componentEvent) { + conversationPanel.calculateOffsetToShowLastMessage(); + } + }); + } + + + + public void addMessage() { + //panel.setImage("image1.jpg"); + //panel_1.setImage("image2.png"); + if (textFieldMsg.getText().length()>0) { + messages.add(new Message(Color.RED,textFieldMsg.getText(),SwingConstants.RIGHT)); + conversationPanel.repaint(); + textFieldMsg.setText(""); + } + } + + private void up(int x) { + conversationPanel.up(x); + conversationPanel.repaint(); + + } + + private void down(int x) { + conversationPanel.down(x); + conversationPanel.repaint(); + } + + @Override + public void mouseDragged(MouseEvent e) { + int y = e.getY(); + if (y < previousY) { + if (Math.abs(previousY-y)<60) + up(Math.abs(previousY-y)); + + } else if (y > previousY) { + if (Math.abs(previousY-y)<60) + down(Math.abs(previousY-y)); + } + previousY = y; + } + + @Override + public void mouseMoved(MouseEvent e) { + } + + + +} diff --git a/src/packageIHM/Message.java b/src/packageIHM/Message.java new file mode 100644 index 0000000..4389fc8 --- /dev/null +++ b/src/packageIHM/Message.java @@ -0,0 +1,68 @@ +package packageIHM; +import java.awt.Color; + +import javax.swing.JTextArea; + +public class Message { + + private Color color; + private String message; + private int side; + private JTextArea bubble; + private int posY; + + public Message(Color color, String message, int side) { + super(); + this.color = color; + this.message = message; + this.side = side; + this.posY = -1; + bubble = null; + } + + public Color getColor() { + return color; + } + + public void setColor(Color color) { + this.color = color; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getSide() { + return side; + } + + public void setSide(int side) { + this.side = side; + } + + public JTextArea getBubble() { + return bubble; + } + + public void setBubble(JTextArea graphics) { + this.bubble = graphics; + } + + public int getPosY() { + return posY; + } + + public void setPosY(int posY) { + this.posY = posY; + } + + + + + + +} diff --git a/src/packageIHM/RedispatchingMouseAdapter.java b/src/packageIHM/RedispatchingMouseAdapter.java new file mode 100644 index 0000000..ed8f3cd --- /dev/null +++ b/src/packageIHM/RedispatchingMouseAdapter.java @@ -0,0 +1,51 @@ +package packageIHM; + +import java.awt.Component; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; + +import javax.swing.SwingUtilities; + +public class RedispatchingMouseAdapter implements MouseListener, MouseWheelListener, MouseMotionListener{ + + public void mouseClicked(MouseEvent e) { + redispatchToParent(e); + } + + public void mousePressed(MouseEvent e) { + redispatchToParent(e); + } + + public void mouseReleased(MouseEvent e) { + redispatchToParent(e); + } + + public void mouseEntered(MouseEvent e) { + redispatchToParent(e); + } + + public void mouseExited(MouseEvent e) { + redispatchToParent(e); + } + + public void mouseWheelMoved(MouseWheelEvent e){ + redispatchToParent(e); + } + + public void mouseDragged(MouseEvent e){ + redispatchToParent(e); + } + + public void mouseMoved(MouseEvent e) { + redispatchToParent(e); + } + + private void redispatchToParent(MouseEvent e){ + Component source = (Component) e.getSource(); + MouseEvent parentEvent = SwingUtilities.convertMouseEvent(source, e, source.getParent()); + source.getParent().dispatchEvent(parentEvent); + } +} \ No newline at end of file diff --git a/src/packageIHM/TextBubble.java b/src/packageIHM/TextBubble.java new file mode 100644 index 0000000..c05e713 --- /dev/null +++ b/src/packageIHM/TextBubble.java @@ -0,0 +1,165 @@ +package packageIHM; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.geom.Area; +import java.awt.geom.RoundRectangle2D; + +import javax.swing.SwingConstants; +import javax.swing.border.AbstractBorder; + +class TextBubble extends AbstractBorder { + private static final long serialVersionUID = 1L; + private Color color; + private int thickness; + private int radius; + private int pointerSize = 0; + private Insets insets = null; + private BasicStroke stroke = null; + private int strokePad; + private double pointerPadPercent = 0.5; + int pointerSide = SwingConstants.TOP; + private RenderingHints hints; + + + TextBubble(Color color, int thickness, int radius, int pointerSize, int pointerSide) { + this.color = color; + this.thickness = thickness; + this.radius = radius; + this.pointerSize = pointerSize; + + hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); + insets = new Insets(0, 0, 0, 0); + this.pointerSide = pointerSide; + + setThickness(thickness); + } + + + public TextBubble setThickness(int n) { + thickness = n < 0? 0: n; + + stroke = new BasicStroke(thickness); + strokePad = thickness / 2; + + setPointerSize(pointerSize); + return this; + } + + public TextBubble setPointerSize(int size) { + int pad = radius/2 + strokePad; + + int pointerSidePad = pad + pointerSize + strokePad; + int left, right, bottom, top; + left = right = bottom = top = pad; + switch (pointerSide) { + case SwingConstants.TOP: + top = pointerSidePad; + break; + case SwingConstants.LEFT: + left = pointerSidePad; + break; + case SwingConstants.RIGHT: + right = pointerSidePad; + break; + default: + case SwingConstants.BOTTOM: + bottom = pointerSidePad; + break; + } + insets.set(top, left, bottom, right); + return this; + } + + + + @Override + public Insets getBorderInsets(Component c) { + return insets; + } + + + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + Color bg = new Color(color.getRed(), color.getGreen(), color.getBlue(), 125); + g.setColor(bg); + g.fillRect(0, 0, width, height); + + Graphics2D g2 = (Graphics2D) g; + g2.setBackground(c.getBackground()); + + + RoundRectangle2D.Double bubble; + Polygon pointer = new Polygon(); + { + int rx, ry, rw, rh; + rx = ry = strokePad; + rw = width - thickness; + rh = height - thickness; + switch (pointerSide) { + case SwingConstants.LEFT: + rx += pointerSize; + case SwingConstants.RIGHT: + rw -= pointerSize; + break; + case SwingConstants.TOP: + ry += pointerSize; + case SwingConstants.BOTTOM: + default: + rh -= pointerSize; + break; + } + bubble = new RoundRectangle2D.Double(rx,ry,rw,rh,radius, radius); + int pointerPad; + if (pointerSide == SwingConstants.LEFT || pointerSide == SwingConstants.RIGHT) { + pointerPad = (int) (pointerPadPercent * (height-radius*2-pointerSize)); + } else { + pointerPad = (int) (pointerPadPercent * (width-radius*2-pointerSize)); + } + + int basePad = strokePad + radius + pointerPad; + switch (pointerSide) { + case SwingConstants.LEFT: + pointer.addPoint(rx, basePad);// top + pointer.addPoint(rx, basePad+pointerSize);// bottom + pointer.addPoint(strokePad, basePad+pointerSize/2); + break; + case SwingConstants.RIGHT: + pointer.addPoint(rw, basePad);// top + pointer.addPoint(rw, basePad+pointerSize);// bottom + pointer.addPoint(width-strokePad, basePad+pointerSize/2); + break; + case SwingConstants.TOP: + pointer.addPoint(basePad, ry);// left + pointer.addPoint(basePad + pointerSize, ry);// right + pointer.addPoint(basePad + (pointerSize / 2), strokePad); + break; + default: + case SwingConstants.BOTTOM: + pointer.addPoint(basePad, rh);// left + pointer.addPoint(basePad + pointerSize, rh);// right + pointer.addPoint(basePad + (pointerSize / 2), height - strokePad); + break; + } + } + + Area area = new Area(bubble); + area.add(new Area(pointer)); + g2.setRenderingHints(hints); + Area spareSpace = new Area(new Rectangle(0, 0, width, height)); + spareSpace.subtract(area); + g2.setClip(spareSpace); + g2.clearRect(0, 0, width, height); + g2.setClip(null); + g2.setColor(color); + g2.setStroke(stroke); + g2.draw(area); + } +} \ No newline at end of file