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:
Kai S. K. Engelbart 2020-03-18 07:10:20 +01:00 committed by GitHub
commit cea4ff1160
10 changed files with 178 additions and 78 deletions

View File

@ -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 -> {

View 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;
}
}

View File

@ -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

View File

@ -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.put(ms, ImageIO.read(MessageListRenderer.class.getResourceAsStream(ms.toString().toLowerCase() + "_icon.png"))); statusIcons = IconUtil.loadByEnum(MessageStatus.class, 16);
forwardIcon = IconUtil.load("/icons/forward.png", 16);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
private JTextArea messageTextArea; private final long senderId;
@Override /**
public JPanel getListCellComponent(ComponentList<? extends Message> list, Message value, boolean isSelected) { * Initializes a message list renderer. Messages with the given sender ID will
final JPanel panel = new JPanel(); * be aligned on the right side, while all other messages will be aligned on
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); * the left side
final Theme theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()); *
* @param senderId the sender ID of the messages to align on the right side
// Panel background * @since Envoy v0.1-beta
panel.setBackground(isSelected ? theme.getSelectionColor() : theme.getCellColor()); */
public MessageListRenderer(long senderId) { this.senderId = senderId; }
// TODO: Handle message attachments // TODO: Handle message attachments
final String state = value.getStatus().toString(); @Override
final String date = new SimpleDateFormat("dd.MM.yyyy HH:mm").format(value.getCreationDate()); public JPanel getListCellComponent(ComponentList<? extends Message> list, Message message, boolean isSelected) {
final String text = value.getText(); final var theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme());
// The Label that displays the creation date of a message // Panel
JLabel dateLabel = new JLabel(date); final var panel = new JPanel();
// Set the date color to be the value of DateColorChat 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.setForeground(theme.getDateColor());
dateLabel.setAlignmentX(1f);
dateLabel.setFont(new Font("Arial", Font.PLAIN, 12));
dateLabel.setPreferredSize(dateLabel.getPreferredSize());
panel.add(dateLabel); var gbc_dateLabel = new GridBagConstraints();
gbc_dateLabel.fill = GridBagConstraints.BOTH;
gbc_dateLabel.gridx = 0;
gbc_dateLabel.gridy = 0;
panel.add(dateLabel, gbc_dateLabel);
if (value.isForwarded()) try { // Message area - The JTextArea that displays the text content of a message.
var forwardLabel = new JLabel("Forwarded", new ImageIcon(ClassLoader.getSystemResourceAsStream(null).readAllBytes()), var messageTextArea = new JTextArea(message.getText());
SwingConstants.CENTER);
forwardLabel.setBackground(panel.getBackground());
forwardLabel.setForeground(Color.lightGray);
panel.add(forwardLabel);
} catch (IOException e) {
e.printStackTrace();
}
// The JTextArea that displays the text content of a message and its status
messageTextArea = new JTextArea(text + System.getProperty("line.separator"));
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;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB