Merge pull request #118 from informatik-ag-ngl/f/icon_util
Added icons and rendering them in the message list
This commit is contained in:
commit
cea4ff1160
@ -65,7 +65,7 @@ public class ChatWindow extends JFrame {
|
|||||||
private PrimaryTextArea messageEnterTextArea = new PrimaryTextArea(space);
|
private PrimaryTextArea messageEnterTextArea = new PrimaryTextArea(space);
|
||||||
private JList<User> userList = new JList<>();
|
private JList<User> userList = new JList<>();
|
||||||
private DefaultListModel<User> userListModel = new DefaultListModel<>();
|
private DefaultListModel<User> userListModel = new DefaultListModel<>();
|
||||||
private ComponentList<Message> messageList = new ComponentList<>(new MessageListRenderer());
|
private ComponentList<Message> messageList = new ComponentList<>();
|
||||||
private PrimaryScrollPane scrollPane = new PrimaryScrollPane();
|
private PrimaryScrollPane scrollPane = new PrimaryScrollPane();
|
||||||
private JTextPane textPane = new JTextPane();
|
private JTextPane textPane = new JTextPane();
|
||||||
private PrimaryButton postButton = new PrimaryButton("Post");
|
private PrimaryButton postButton = new PrimaryButton("Post");
|
||||||
@ -103,6 +103,7 @@ public class ChatWindow extends JFrame {
|
|||||||
public ChatWindow() {
|
public ChatWindow() {
|
||||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
setBounds(100, 100, 600, 800);
|
setBounds(100, 100, 600, 800);
|
||||||
|
setMinimumSize(new Dimension(400, 300));
|
||||||
setTitle("Envoy");
|
setTitle("Envoy");
|
||||||
setLocationRelativeTo(null);
|
setLocationRelativeTo(null);
|
||||||
setIconImage(Toolkit.getDefaultToolkit().createImage(getClass().getClassLoader().getResource("envoy_logo.png")));
|
setIconImage(Toolkit.getDefaultToolkit().createImage(getClass().getClassLoader().getResource("envoy_logo.png")));
|
||||||
@ -129,9 +130,12 @@ public class ChatWindow extends JFrame {
|
|||||||
scrollPane.setViewportView(messageList);
|
scrollPane.setViewportView(messageList);
|
||||||
scrollPane.addComponentListener(new ComponentAdapter() {
|
scrollPane.addComponentListener(new ComponentAdapter() {
|
||||||
|
|
||||||
// updates list elements when list is resized
|
// Update list elements when scroll pane (and thus list) is resized
|
||||||
@Override
|
@Override
|
||||||
public void componentResized(ComponentEvent e) { messageList.synchronizeModel(); }
|
public void componentResized(ComponentEvent e) {
|
||||||
|
messageList.setMaximumSize(new Dimension(scrollPane.getWidth(), Integer.MAX_VALUE));
|
||||||
|
messageList.synchronizeModel();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||||
|
|
||||||
@ -608,6 +612,8 @@ public class ChatWindow extends JFrame {
|
|||||||
this.localDb = localDb;
|
this.localDb = localDb;
|
||||||
this.writeProxy = writeProxy;
|
this.writeProxy = writeProxy;
|
||||||
|
|
||||||
|
messageList.setRenderer(new MessageListRenderer(client.getSender().getId()));
|
||||||
|
|
||||||
// Load users and chats
|
// Load users and chats
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
localDb.getUsers().values().forEach(user -> {
|
localDb.getUsers().values().forEach(user -> {
|
||||||
|
61
src/main/java/envoy/client/ui/IconUtil.java
Normal file
61
src/main/java/envoy/client/ui/IconUtil.java
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package envoy.client.ui;
|
||||||
|
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides static utility methods for loading icons from the resource
|
||||||
|
* folder.<br>
|
||||||
|
* <br>
|
||||||
|
* Project: <strong>envoy-client</strong>
|
||||||
|
* File: <strong>IconUtil.java</strong>
|
||||||
|
* Created: <strong>16.03.2020</strong>
|
||||||
|
*
|
||||||
|
* @author Kai S. K. Engelbart
|
||||||
|
* @since Envoy v0.1-beta
|
||||||
|
*/
|
||||||
|
public class IconUtil {
|
||||||
|
|
||||||
|
private IconUtil() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads an icon from resource folder and scales it to a given size.
|
||||||
|
*
|
||||||
|
* @param path the path to the icon inside the resource folder
|
||||||
|
* @param size the size to scale the icon to
|
||||||
|
* @return the scaled icon
|
||||||
|
* @throws IOException if the loading process failed
|
||||||
|
* @since Envoy v0.1-beta
|
||||||
|
*/
|
||||||
|
public static ImageIcon load(String path, int size) throws IOException {
|
||||||
|
return new ImageIcon(ImageIO.read(IconUtil.class.getResourceAsStream(path)).getScaledInstance(size, size, Image.SCALE_SMOOTH));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Loads icons specified by an enum. The images have to be named like the
|
||||||
|
* lowercase enum constants with {@code .png} extension and be located inside a
|
||||||
|
* folder with the lowercase name of the enum, which must be contained inside
|
||||||
|
* the {@code /icons} folder.
|
||||||
|
*
|
||||||
|
* @param <T> the enum that specifies the icons to load
|
||||||
|
* @param enumClass the class of the enum
|
||||||
|
* @param size the size to scale the icons to
|
||||||
|
* @return a map containing the loaded icons with the corresponding enum
|
||||||
|
* constants as keys
|
||||||
|
* @throws IOException if the loading process failed
|
||||||
|
* @since Envoy v0.1-beta
|
||||||
|
*/
|
||||||
|
public static <T extends Enum<T>> EnumMap<T, ImageIcon> loadByEnum(Class<T> enumClass, int size) throws IOException {
|
||||||
|
var icons = new EnumMap<T, ImageIcon>(enumClass);
|
||||||
|
var path = "/icons/" + enumClass.getSimpleName().toLowerCase() + "/";
|
||||||
|
for (var e : EnumSet.allOf(enumClass))
|
||||||
|
icons.put(e, load(path + e.toString().toLowerCase() + ".png", size));
|
||||||
|
return icons;
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,8 @@ public class ComponentList<E> extends JPanel {
|
|||||||
|
|
||||||
private static final long serialVersionUID = 1759644503942876737L;
|
private static final long serialVersionUID = 1759644503942876737L;
|
||||||
|
|
||||||
|
public ComponentList() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance of {@link ComponentList}.
|
* Creates an instance of {@link ComponentList}.
|
||||||
*
|
*
|
||||||
@ -39,8 +41,8 @@ public class ComponentList<E> extends JPanel {
|
|||||||
* @since Envoy v0.3-alpha
|
* @since Envoy v0.3-alpha
|
||||||
*/
|
*/
|
||||||
public ComponentList(ComponentListCellRenderer<E> renderer) {
|
public ComponentList(ComponentListCellRenderer<E> renderer) {
|
||||||
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
this();
|
||||||
this.renderer = renderer;
|
setRenderer(renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,7 +55,6 @@ public class ComponentList<E> extends JPanel {
|
|||||||
*/
|
*/
|
||||||
public ComponentList(ComponentListModel<E> model, ComponentListCellRenderer<E> renderer) {
|
public ComponentList(ComponentListModel<E> model, ComponentListCellRenderer<E> renderer) {
|
||||||
this(renderer);
|
this(renderer);
|
||||||
this.model = model;
|
|
||||||
setModel(model);
|
setModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,6 +201,18 @@ public class ComponentList<E> extends JPanel {
|
|||||||
*/
|
*/
|
||||||
public ComponentListModel<E> getModel() { return model; }
|
public ComponentListModel<E> getModel() { return model; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the renderer
|
||||||
|
* @since Envoy v0.1-beta
|
||||||
|
*/
|
||||||
|
public ComponentListCellRenderer<E> getRenderer() { return renderer; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param renderer the renderer to set
|
||||||
|
* @since Envoy v0.1-beta
|
||||||
|
*/
|
||||||
|
public void setRenderer(ComponentListCellRenderer<E> renderer) { this.renderer = renderer; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the multipleSelectionEnabled
|
* @return the multipleSelectionEnabled
|
||||||
* @since Envoy v0.1-beta
|
* @since Envoy v0.1-beta
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
package envoy.client.ui.renderer;
|
package envoy.client.ui.renderer;
|
||||||
|
|
||||||
import java.awt.Font;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import envoy.client.data.Settings;
|
import envoy.client.data.Settings;
|
||||||
import envoy.client.ui.Color;
|
import envoy.client.ui.Color;
|
||||||
import envoy.client.ui.Theme;
|
import envoy.client.ui.IconUtil;
|
||||||
import envoy.client.ui.list.ComponentList;
|
import envoy.client.ui.list.ComponentList;
|
||||||
import envoy.client.ui.list.ComponentListCellRenderer;
|
import envoy.client.ui.list.ComponentListCellRenderer;
|
||||||
import envoy.data.Message;
|
import envoy.data.Message;
|
||||||
@ -31,89 +29,111 @@ import envoy.data.Message.MessageStatus;
|
|||||||
*/
|
*/
|
||||||
public class MessageListRenderer implements ComponentListCellRenderer<Message> {
|
public class MessageListRenderer implements ComponentListCellRenderer<Message> {
|
||||||
|
|
||||||
private static final EnumMap<MessageStatus, BufferedImage> statusIcons = new EnumMap<>(MessageStatus.class);
|
private static EnumMap<MessageStatus, ImageIcon> statusIcons;
|
||||||
|
private static ImageIcon forwardIcon;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
for (MessageStatus ms : MessageStatus.values())
|
try {
|
||||||
try {
|
statusIcons = IconUtil.loadByEnum(MessageStatus.class, 16);
|
||||||
statusIcons.put(ms, ImageIO.read(MessageListRenderer.class.getResourceAsStream(ms.toString().toLowerCase() + "_icon.png")));
|
forwardIcon = IconUtil.load("/icons/forward.png", 16);
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private JTextArea messageTextArea;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JPanel getListCellComponent(ComponentList<? extends Message> list, Message value, boolean isSelected) {
|
|
||||||
final JPanel panel = new JPanel();
|
|
||||||
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
|
|
||||||
final Theme theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme());
|
|
||||||
|
|
||||||
// Panel background
|
|
||||||
panel.setBackground(isSelected ? theme.getSelectionColor() : theme.getCellColor());
|
|
||||||
|
|
||||||
// TODO: Handle message attachments
|
|
||||||
|
|
||||||
final String state = value.getStatus().toString();
|
|
||||||
final String date = new SimpleDateFormat("dd.MM.yyyy HH:mm").format(value.getCreationDate());
|
|
||||||
final String text = value.getText();
|
|
||||||
|
|
||||||
// The Label that displays the creation date of a message
|
|
||||||
JLabel dateLabel = new JLabel(date);
|
|
||||||
// Set the date color to be the value of DateColorChat
|
|
||||||
dateLabel.setForeground(theme.getDateColor());
|
|
||||||
|
|
||||||
panel.add(dateLabel);
|
|
||||||
|
|
||||||
if (value.isForwarded()) try {
|
|
||||||
var forwardLabel = new JLabel("Forwarded", new ImageIcon(ClassLoader.getSystemResourceAsStream(null).readAllBytes()),
|
|
||||||
SwingConstants.CENTER);
|
|
||||||
forwardLabel.setBackground(panel.getBackground());
|
|
||||||
forwardLabel.setForeground(Color.lightGray);
|
|
||||||
panel.add(forwardLabel);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The JTextArea that displays the text content of a message and its status
|
private final long senderId;
|
||||||
messageTextArea = new JTextArea(text + System.getProperty("line.separator"));
|
|
||||||
|
/**
|
||||||
|
* Initializes a message list renderer. Messages with the given sender ID will
|
||||||
|
* be aligned on the right side, while all other messages will be aligned on
|
||||||
|
* the left side
|
||||||
|
*
|
||||||
|
* @param senderId the sender ID of the messages to align on the right side
|
||||||
|
* @since Envoy v0.1-beta
|
||||||
|
*/
|
||||||
|
public MessageListRenderer(long senderId) { this.senderId = senderId; }
|
||||||
|
|
||||||
|
// TODO: Handle message attachments
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JPanel getListCellComponent(ComponentList<? extends Message> list, Message message, boolean isSelected) {
|
||||||
|
final var theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme());
|
||||||
|
|
||||||
|
// Panel
|
||||||
|
final var panel = new JPanel();
|
||||||
|
final int padding = (int) (list.getWidth() * 0.35);
|
||||||
|
|
||||||
|
GridBagLayout gbl_panel = new GridBagLayout();
|
||||||
|
gbl_panel.columnWidths = new int[] { 1, 1 };
|
||||||
|
gbl_panel.rowHeights = new int[] { 1, 1 };
|
||||||
|
gbl_panel.columnWeights = new double[] { 1, 1 };
|
||||||
|
gbl_panel.rowWeights = new double[] { 1, 1 };
|
||||||
|
|
||||||
|
panel.setLayout(gbl_panel);
|
||||||
|
panel.setBackground(isSelected ? theme.getSelectionColor() : theme.getCellColor());
|
||||||
|
|
||||||
|
// Date Label - The Label that displays the creation date of a message
|
||||||
|
var dateLabel = new JLabel(new SimpleDateFormat("dd.MM.yyyy HH:mm").format(message.getCreationDate()));
|
||||||
|
dateLabel.setForeground(theme.getDateColor());
|
||||||
|
dateLabel.setAlignmentX(1f);
|
||||||
|
dateLabel.setFont(new Font("Arial", Font.PLAIN, 12));
|
||||||
|
dateLabel.setPreferredSize(dateLabel.getPreferredSize());
|
||||||
|
|
||||||
|
var gbc_dateLabel = new GridBagConstraints();
|
||||||
|
gbc_dateLabel.fill = GridBagConstraints.BOTH;
|
||||||
|
gbc_dateLabel.gridx = 0;
|
||||||
|
gbc_dateLabel.gridy = 0;
|
||||||
|
panel.add(dateLabel, gbc_dateLabel);
|
||||||
|
|
||||||
|
// Message area - The JTextArea that displays the text content of a message.
|
||||||
|
var messageTextArea = new JTextArea(message.getText());
|
||||||
messageTextArea.setLineWrap(true);
|
messageTextArea.setLineWrap(true);
|
||||||
messageTextArea.setWrapStyleWord(true);
|
messageTextArea.setWrapStyleWord(true);
|
||||||
messageTextArea.setAlignmentX(0.5f);
|
|
||||||
messageTextArea.setForeground(theme.getMessageTextColor());
|
messageTextArea.setForeground(theme.getMessageTextColor());
|
||||||
messageTextArea.setBackground(panel.getBackground());
|
messageTextArea.setAlignmentX(0.5f);
|
||||||
|
messageTextArea.setBackground(theme.getCellColor());
|
||||||
messageTextArea.setEditable(false);
|
messageTextArea.setEditable(false);
|
||||||
|
var font = new Font("Arial", Font.PLAIN, 14);
|
||||||
|
messageTextArea.setFont(font);
|
||||||
|
messageTextArea.setSize(list.getMaximumSize().width - padding - 16, 10);
|
||||||
|
|
||||||
panel.add(messageTextArea);
|
var gbc_messageTextArea = new GridBagConstraints();
|
||||||
|
gbc_messageTextArea.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
gbc_messageTextArea.gridx = 0;
|
||||||
|
gbc_messageTextArea.gridy = 1;
|
||||||
|
panel.add(messageTextArea, gbc_messageTextArea);
|
||||||
|
|
||||||
JLabel statusLabel = new JLabel(state);
|
// Status Label - displays the status of the message
|
||||||
statusLabel.setFont(new Font("Arial", Font.BOLD, 14));
|
var statusLabel = new JLabel(statusIcons.get(message.getStatus()));
|
||||||
Color statusColor;
|
|
||||||
switch (value.getStatus()) {
|
var gbc_statusLabel = new GridBagConstraints();
|
||||||
case WAITING:
|
gbc_statusLabel.gridx = 1;
|
||||||
statusColor = Color.gray;
|
gbc_statusLabel.gridy = 1;
|
||||||
break;
|
panel.add(statusLabel, gbc_statusLabel);
|
||||||
case SENT:
|
|
||||||
statusColor = Color.blue;
|
// Forwarding
|
||||||
break;
|
if (message.isForwarded()) {
|
||||||
case RECEIVED:
|
var forwardLabel = new JLabel("Forwarded", forwardIcon, SwingConstants.CENTER);
|
||||||
statusColor = Color.yellow;
|
forwardLabel.setBackground(panel.getBackground());
|
||||||
break;
|
forwardLabel.setForeground(Color.lightGray);
|
||||||
case READ:
|
|
||||||
statusColor = Color.green;
|
var gbc_forwardLabel = new GridBagConstraints();
|
||||||
break;
|
gbc_forwardLabel.fill = GridBagConstraints.BOTH;
|
||||||
default:
|
gbc_forwardLabel.gridx = 1;
|
||||||
statusColor = theme.getMessageTextColor();
|
gbc_forwardLabel.gridy = 0;
|
||||||
break;
|
panel.add(forwardLabel, gbc_forwardLabel);
|
||||||
}
|
}
|
||||||
statusLabel.setForeground(statusColor);
|
|
||||||
statusLabel.setBackground(panel.getBackground());
|
|
||||||
|
|
||||||
panel.add(statusLabel);
|
// Define an etched border and some space to the messages below
|
||||||
|
var ours = senderId == message.getSenderId();
|
||||||
|
panel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, ours ? padding : 0, 10, ours ? 0 : padding),
|
||||||
|
BorderFactory.createEtchedBorder()));
|
||||||
|
|
||||||
// Define some space to the messages below
|
var size = new Dimension(list.getMaximumSize().width - 50, panel.getPreferredSize().height);
|
||||||
panel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(), BorderFactory.createEtchedBorder()));
|
|
||||||
|
panel.setPreferredSize(size);
|
||||||
|
panel.setMinimumSize(size);
|
||||||
|
panel.setMaximumSize(size);
|
||||||
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
BIN
src/main/resources/icons/forward.png
Normal file
BIN
src/main/resources/icons/forward.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
BIN
src/main/resources/icons/messagestatus/read.png
Normal file
BIN
src/main/resources/icons/messagestatus/read.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
src/main/resources/icons/messagestatus/received.png
Normal file
BIN
src/main/resources/icons/messagestatus/received.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
src/main/resources/icons/messagestatus/sent.png
Normal file
BIN
src/main/resources/icons/messagestatus/sent.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
BIN
src/main/resources/icons/messagestatus/waiting.png
Normal file
BIN
src/main/resources/icons/messagestatus/waiting.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
BIN
src/main/resources/icons/settings.png
Normal file
BIN
src/main/resources/icons/settings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
Reference in New Issue
Block a user