Fixed bug adding a line break when sending via enter

* additionally merged full input of f/enhanced_component_list with the
current working state of f/forward_messages:
* implemented setting of colors (theming) for ContextMenu
* renamed messageTextColor to textColor
This commit is contained in:
delvh 2020-03-21 19:26:11 +01:00
parent e07c8ecaf7
commit 8c55f67eb0
11 changed files with 325 additions and 40 deletions

View File

@ -1,4 +1,4 @@
package envoy.client.ui;
package envoy.client;
import java.awt.EventQueue;
import java.io.File;
@ -15,6 +15,7 @@ import javax.swing.SwingUtilities;
import envoy.client.data.*;
import envoy.client.net.Client;
import envoy.client.net.WriteProxy;
import envoy.client.ui.StatusTrayIcon;
import envoy.client.ui.container.ChatWindow;
import envoy.client.ui.container.LoginDialog;
import envoy.data.Config;
@ -129,7 +130,7 @@ public class Startup {
// Save all users to the local database and flush cache
localDb.setUsers(client.getUsers());
writeProxy.flushCache();
} else
} else
// Set all contacts to offline mode
localDb.getUsers().values().stream().filter(u -> u != localDb.getUser()).forEach(u -> u.setStatus(UserStatus.OFFLINE));

9
src/main/java/envoy/client/ui/Theme.java Normal file → Executable file
View File

@ -29,7 +29,8 @@ public class Theme implements Serializable {
* elements
* @param interactableBackgroundColor the color of interactable background UI
* elements
* @param messageColorChat the color of chat messages
* @param textColor the color normal text should be displayed
* in
* @param dateColorChat the color of chat message metadata
* @param selectionColor the section color
* @param typingMessageColor the color of currently typed messages
@ -37,7 +38,7 @@ public class Theme implements Serializable {
* @since Envoy v0.2-alpha
*/
public Theme(String themeName, Color backgroundColor, Color cellColor, Color interactableForegroundColor, Color interactableBackgroundColor,
Color messageColorChat, Color dateColorChat, Color selectionColor, Color typingMessageColor, Color userNameColor) {
Color textColor, Color dateColorChat, Color selectionColor, Color typingMessageColor, Color userNameColor) {
this.themeName = themeName;
@ -45,7 +46,7 @@ public class Theme implements Serializable {
colors.put("cellColor", cellColor);
colors.put("interactableForegroundColor", interactableForegroundColor);
colors.put("interactableBackgroundColor", interactableBackgroundColor);
colors.put("messageColorChat", messageColorChat);
colors.put("textColor", textColor);
colors.put("dateColorChat", dateColorChat);
colors.put("selectionColor", selectionColor);
colors.put("typingMessageColor", typingMessageColor);
@ -89,7 +90,7 @@ public class Theme implements Serializable {
* displayed
* @since Envoy v0.2-alpha
*/
public Color getMessageTextColor() { return colors.get("messageColorChat"); }
public Color getTextColor() { return colors.get("textColor"); }
/**
* @return the {@link Color} in which the creation date of a message should be

View File

@ -22,10 +22,12 @@ import envoy.client.event.MessageCreationEvent;
import envoy.client.event.ThemeChangeEvent;
import envoy.client.net.Client;
import envoy.client.net.WriteProxy;
import envoy.client.ui.*;
import envoy.client.ui.Theme;
import envoy.client.ui.list.ComponentList;
import envoy.client.ui.list.ComponentList.SelectionMode;
import envoy.client.ui.list.Model;
import envoy.client.ui.list_component.ContactSearchComponent;
import envoy.client.ui.list_component.MessageComponent;
import envoy.client.ui.primary.PrimaryButton;
import envoy.client.ui.primary.PrimaryScrollPane;
import envoy.client.ui.primary.PrimaryTextArea;
@ -490,7 +492,7 @@ public class ChatWindow extends JFrame {
contentPane.setBackground(theme.getBackgroundColor());
contentPane.setForeground(theme.getUserNameColor());
// messageList
messageList.setForeground(theme.getMessageTextColor());
messageList.setForeground(theme.getTextColor());
messageList.setBackground(theme.getCellColor());
// scrollPane
scrollPane.applyTheme(theme);
@ -525,7 +527,7 @@ public class ChatWindow extends JFrame {
searchField.setForeground(theme.getUserNameColor());
cancelButton.setBackground(theme.getInteractableBackgroundColor());
cancelButton.setForeground(theme.getInteractableForegroundColor());
contactList.setForeground(theme.getMessageTextColor());
contactList.setForeground(theme.getTextColor());
contactList.setBackground(theme.getCellColor());
scrollForPossibleContacts.applyTheme(theme);
}
@ -540,11 +542,15 @@ public class ChatWindow extends JFrame {
JOptionPane.showMessageDialog(this, "Please select a recipient!", "Cannot send message", JOptionPane.INFORMATION_MESSAGE);
return;
}
String text = messageEnterTextArea.getText().trim();
if (!text.isEmpty()) checkMessageTextLength();
// delete final line break, if present (especially if "Enter" is used to send
// the message)
if (text.endsWith(System.getProperty("line.separator"))) text = text.substring(0, text.lastIndexOf(System.getProperty("line.separator")));
if (!messageEnterTextArea.getText().isEmpty()) checkMessageTextLength();
// Create message
final Message message = new MessageBuilder(localDb.getUser().getId(), currentChat.getRecipient().getId(), localDb.getIdGenerator())
.setText(messageEnterTextArea.getText())
.setText(text)
.build();
sendMessage(message);
// Clear text field

View File

@ -14,7 +14,6 @@ import javax.swing.border.EmptyBorder;
import envoy.client.data.Settings;
import envoy.client.net.Client;
import envoy.client.ui.Theme;
import envoy.client.ui.UserComponent;
import envoy.client.ui.list.ComponentList;
import envoy.client.ui.list.ComponentList.SelectionMode;
import envoy.client.ui.list.Model;
@ -35,7 +34,7 @@ public class ContactsChooserDialog extends JDialog {
private static final long serialVersionUID = -5774558118579032256L;
private ComponentList<User> contactList = new ComponentList<User>().setRenderer(UserComponent::new);
private ComponentList<User> contactList = new ComponentList<>();
private JButton okButton = new JButton("Ok");
private JButton cancelButton = new JButton("Cancel");
@ -77,7 +76,7 @@ public class ContactsChooserDialog extends JDialog {
dialog.setVisible(true);
dialog.repaint();
dialog.revalidate();
return results;
return results.size() > 0 ? results : null;
}
/**
@ -85,12 +84,11 @@ public class ContactsChooserDialog extends JDialog {
*/
private ContactsChooserDialog() {
contactList.setSelectionMode(SelectionMode.MULTIPLE);
contactList
.setSelectionHandler((user, comp, isSelected) -> comp.setBackground(isSelected ? theme.getSelectionColor() : theme.getCellColor()));
// setBounds(100, 100, 450, 300);
setModal(true);
getContentPane().setLayout(new BorderLayout());
setBackground(theme.getBackgroundColor());
setForeground(theme.getMessageTextColor());
setForeground(theme.getTextColor());
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
getContentPane().add(contentPanel, BorderLayout.CENTER);
contentPanel.setLayout(new BorderLayout(0, 0));

View File

@ -0,0 +1,246 @@
package envoy.client.ui.container;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;
import envoy.client.ui.Theme;
/**
* This class defines a menu that will be automatically called if
* {@link MouseEvent#isPopupTrigger()} returns true for the parent component.
* The user has the possibility to directly add actions to be performed when
* clicking on the element with the selected String. Additionally, for each
* element an {@link Icon} can be added, but it must not be.
* If the key(text) of an element starts with one of the predefined values, a
* special component will be called: either a {@link JRadioButtonMenuItem}, a
* {@link JCheckBoxMenuItem} or a {@link JMenu} will be created.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>ContextMenu.java</strong><br>
* Created: <strong>17 Mar 2020</strong><br>
*
* @author Leon Hofmeister
* @since Envoy v0.1-beta
*/
public class ContextMenu extends JPopupMenu {
private static final long serialVersionUID = 2177146471226992104L;
/**
* If a key starts with this String, a {@link JCheckBoxMenuItem} will be created
*/
public static final String checkboxMenuItem = "ChBoMI";
/**
* If a key starts with this String, a {@link JRadioButtonMenuItem} will be
* created
*/
public static final String radioButtonMenuItem = "RaBuMI";
/**
* If a key starts with this String, a {@link JMenu} will be created
*/
public static final String subMenuItem = "SubMI";
private Map<String, ActionListener> items = new HashMap<>();
private Map<String, Icon> icons = new HashMap<>();
private Map<String, Integer> mnemonics = new HashMap<>();
private ButtonGroup radioButtonGroup = new ButtonGroup();
private boolean built = false;
private boolean visible = false;
/**
* @param parent the component which will call this
* {@link ContextMenu}
* @since Envoy v0.1-beta
*/
public ContextMenu(Component parent) { setInvoker(parent); }
/**
* @param label the string that a UI may use to display as a title
* for the pop-up menu
* @param parent the component which will call this
* {@link ContextMenu}
* @param itemsWithActions a map of all strings to be displayed with according
* actions
* @param itemIcons the icons to be displayed before a name, if wanted.
* Only keys in here will have an Icon displayed. More
* precisely, all keys here not included in the first
* map will be thrown out.
* @param itemMnemonics the keyboard shortcuts that need to be pressed to
* automatically execute the {@link JMenuItem} with the
* given text
* @since Envoy v0.1-beta
*/
public ContextMenu(String label, Component parent, Map<String, ActionListener> itemsWithActions, Map<String, Icon> itemIcons,
Map<String, Integer> itemMnemonics) {
super(label);
setInvoker(parent);
this.items = (itemsWithActions != null) ? itemsWithActions : items;
this.icons = (itemIcons != null) ? itemIcons : icons;
this.mnemonics = (itemMnemonics != null) ? itemMnemonics : mnemonics;
}
/**
* Prepares the PopupMenu to be displayed. Should only be used once all map
* values have been set.
*
* @return this instance of {@link ContextMenu} to allow chaining behind the
* constructor
* @since Envoy v0.1-beta
*/
public ContextMenu build() {
items.forEach((text, action) -> {
// case radio button wanted
AbstractButton item;
if (text.startsWith(radioButtonMenuItem)) {
item = new JRadioButtonMenuItem(text.substring(radioButtonMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
radioButtonGroup.add(item);
// case check box wanted
} else if (text.startsWith(checkboxMenuItem))
item = new JCheckBoxMenuItem(text.substring(checkboxMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
// case sub-menu wanted
else if (text.startsWith(subMenuItem)) item = new JMenu(text.substring(subMenuItem.length()));
else // normal JMenuItem wanted
item = new JMenuItem(text, icons.containsKey(text) ? icons.get(text) : null);
item.addActionListener(action);
if (mnemonics.containsKey(text)) item.setMnemonic(mnemonics.get(text));
add(item);
});
getInvoker().addMouseListener(getShowingListener());
built = true;
return this;
}
/**
* @param label the string that a UI may use to display as a title for the
* pop-up menu.
* @since Envoy v0.1-beta
*/
public ContextMenu(String label) { super(label); }
private MouseAdapter getShowingListener() {
return new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) { action(e); }
@Override
public void mousePressed(MouseEvent e) { action(e); }
@Override
public void mouseReleased(MouseEvent e) { action(e); }
private void action(MouseEvent e) {
if (!built) build();
if (e.isPopupTrigger()) {
// hides the menu if already visible
visible = !visible;
if (visible) show(e.getComponent(), e.getX(), e.getY());
else setVisible(false);
}
}
};
}
/**
* Removes all subcomponents of this menu.
*
* @since Envoy v0.1-beta
*/
public void clear() {
removeAll();
items = new HashMap<>();
icons = new HashMap<>();
mnemonics = new HashMap<>();
}
/**
* @return the items
* @since Envoy v0.1-beta
*/
public Map<String, ActionListener> getItems() { return items; }
/**
* @param items the items with the displayed text and the according action to
* take once called
* @since Envoy v0.1-beta
*/
public void setItems(Map<String, ActionListener> items) { this.items = items; }
/**
* @return the icons
* @since Envoy v0.1-beta
*/
public Map<String, Icon> getIcons() { return icons; }
/**
* @param icons the icons to set
* @since Envoy v0.1-beta
*/
public void setIcons(Map<String, Icon> icons) { this.icons = icons; }
/**
* @return the mnemonics (the keyboard shortcuts that automatically execute the
* command for a {@link JMenuItem} with corresponding text)
* @since Envoy v0.1-beta
*/
public Map<String, Integer> getMnemonics() { return mnemonics; }
/**
* @param mnemonics the keyboard shortcuts that need to be pressed to
* automatically execute the {@link JMenuItem} with the given
* text
* @since Envoy v0.1-beta
*/
public void setMnemonics(Map<String, Integer> mnemonics) { this.mnemonics = mnemonics; }
/**
* {@inheritDoc}<br>
* Additionally sets the foreground of all subcomponents of this
* {@link ContextMenu}.
*
* @since Envoy v0.1-beta
*/
@Override
public void setForeground(Color color) {
super.setForeground(color);
for (MenuElement element : getSubElements())
((Component) element).setForeground(color);
}
/**
* {@inheritDoc}<br>
* Additionally sets the background of all subcomponents of this
* {@link ContextMenu}.
*
* @since Envoy v0.1-beta
*/
@Override
public void setBackground(Color color) {
super.setBackground(color);
for (MenuElement element : getSubElements())
((Component) element).setBackground(color);
}
/**
* Sets the fore- and background of all elements contained in this
* {@link ContextMenu}
* This method is to be only used by Envoy as {@link Theme} is an
* Envoy-exclusive object.
*
* @param theme the theme to use
* @since Envoy v0.1-beta
*/
protected void applyTheme(Theme theme) {
setBackground(theme.getInteractableBackgroundColor());
setForeground(theme.getTextColor());
}
}

View File

@ -10,6 +10,7 @@ import javax.swing.JComponent;
* Created: <strong>21.03.2020</strong>
*
* @author Kai S. K. Engelbart
* @param <E> the type of the underlying {@link ComponentList}
* @since Envoy v0.1-beta
*/
@FunctionalInterface
@ -17,9 +18,9 @@ public interface SelectionHandler<E> {
/**
* Notifies the handler about a selection.
*
* @param element the selected element
* @param component the selected component
*
* @param element the selected element
* @param component the selected component
* @param isSelected contains the selection state
* @since Envoy v0.1-beta
*/

View File

@ -1,4 +1,4 @@
package envoy.client.ui;
package envoy.client.ui.list_component;
import java.awt.Component;
import java.awt.Dimension;
@ -26,6 +26,11 @@ public class ContactSearchComponent extends JComponent {
private static final long serialVersionUID = 3166795412575239455L;
/**
* @param list the {@link ComponentList} that is used to display search results
* @param user the {@link User} that appears as a search result
* @since Envoy v0.1-beta
*/
public ContactSearchComponent(ComponentList<? extends User> list, User user) {
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
@ -33,7 +38,7 @@ public class ContactSearchComponent extends JComponent {
setForeground(list.getForeground());
JLabel display = new JLabel(user.getName());
display.setForeground(Settings.getInstance().getCurrentTheme().getMessageTextColor());
display.setForeground(Settings.getInstance().getCurrentTheme().getTextColor());
display.setAlignmentX(Component.LEFT_ALIGNMENT);
display.setAlignmentY(Component.CENTER_ALIGNMENT);
display.setFont(new Font("Arial", Font.PLAIN, 16));

View File

@ -1,4 +1,4 @@
package envoy.client.ui;
package envoy.client.ui.list_component;
import java.awt.*;
import java.io.IOException;
@ -7,10 +7,14 @@ import java.util.EnumMap;
import javax.swing.*;
import envoy.client.data.Chat;
import envoy.client.data.Settings;
import envoy.client.ui.Color;
import envoy.client.ui.IconUtil;
import envoy.client.ui.list.ComponentList;
import envoy.data.Message;
import envoy.data.Message.MessageStatus;
import envoy.data.User;
/**
* Project: <strong>envoy-client</strong>
@ -36,11 +40,15 @@ public class MessageComponent extends JPanel {
}
}
/**
* @param list the {@link ComponentList} that displays this {@link Chat}
* @param message the {@link Message} to display
* @param senderId the id of the {@link User} who sends messages from this
* account
* @since Envoy v0.1-beta
*/
public MessageComponent(ComponentList<? extends Message> list, Message message, long senderId) {
this(list.getMaximumSize().width, message, senderId);
}
public MessageComponent(int width, Message message, long senderId) {
var width = list.getMaximumSize().width;
final var theme = Settings.getInstance().getCurrentTheme();
final int padding = (int) (width * 0.35);
@ -70,7 +78,7 @@ public class MessageComponent extends JPanel {
var messageTextArea = new JTextArea(message.getText());
messageTextArea.setLineWrap(true);
messageTextArea.setWrapStyleWord(true);
messageTextArea.setForeground(theme.getMessageTextColor());
messageTextArea.setForeground(theme.getTextColor());
messageTextArea.setAlignmentX(0.5f);
messageTextArea.setBackground(theme.getCellColor());
messageTextArea.setEditable(false);
@ -107,8 +115,7 @@ public class MessageComponent extends JPanel {
// Define an etched border and some space to the messages below
var ours = senderId == message.getSenderId();
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(0, ours ? padding : 10, 10, ours ? 0 : padding),
setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, ours ? padding : 10, 10, ours ? 0 : padding),
BorderFactory.createEtchedBorder()));
var size = new Dimension(width - 50, getPreferredSize().height);

View File

@ -1,4 +1,4 @@
package envoy.client.ui;
package envoy.client.ui.list_component;
import java.awt.BorderLayout;
import java.awt.Dimension;
@ -7,11 +7,14 @@ import javax.swing.JComponent;
import javax.swing.JLabel;
import envoy.client.data.Settings;
import envoy.client.ui.list.ComponentList;
import envoy.client.ui.Color;
import envoy.client.ui.Theme;
import envoy.data.User;
import envoy.data.User.UserStatus;
/**
* Displays a {@link User}.<br>
* <br>
* Project: <strong>envoy-client</strong>
* File: <strong>UserComponent.java</strong>
* Created: <strong>21.03.2020</strong>
@ -23,7 +26,11 @@ public class UserComponent extends JComponent {
private static final long serialVersionUID = 8450602172939729585L;
public UserComponent(ComponentList<? extends User> list, User user) {
/**
* @param user the {@link User} whose information is displayed
* @since Envoy v0.1-beta
*/
public UserComponent(User user) {
final Theme theme = Settings.getInstance().getCurrentTheme();
setLayout(new BorderLayout());

View File

@ -0,0 +1,14 @@
/**
* This package contains swing components that can be displayed by
* {@link envoy.client.ui.list.ComponentList}.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>package-info.java</strong><br>
* Created: <strong>21 Mar 2020</strong><br>
*
* @author Leon Hofmeister
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
* @since Envoy v0.1-beta
*/
package envoy.client.ui.list_component;

View File

@ -38,9 +38,8 @@ public class ThemeCustomizationPanel extends SettingsPanel {
private final Insets insets = new Insets(5, 5, 5, 5);
private static final Settings settings = Settings.getInstance();
private static final Logger logger = EnvoyLog.getLogger(ThemeCustomizationPanel.class);
private static final long serialVersionUID = -8697897390666456624L;
private static final Logger logger = EnvoyLog.getLogger(ThemeCustomizationPanel.class);
private static final long serialVersionUID = -8697897390666456624L;
/**
* Initializes a {@link ThemeCustomizationPanel} that enables the user to change
@ -53,7 +52,7 @@ public class ThemeCustomizationPanel extends SettingsPanel {
*/
public ThemeCustomizationPanel(SettingsScreen parent) {
super(parent);
temporaryTheme = new Theme("temporaryTheme", settings.getCurrentTheme());
temporaryTheme = new Theme("temporaryTheme", Settings.getInstance().getCurrentTheme());
GridBagLayout gbl_themeLayout = new GridBagLayout();
@ -64,7 +63,7 @@ public class ThemeCustomizationPanel extends SettingsPanel {
setLayout(gbl_themeLayout);
themes.setSelectedItem(settings.getCurrentTheme());
themes.setSelectedItem(Settings.getInstance().getCurrentTheme());
GridBagConstraints gbc_themes = new GridBagConstraints();
gbc_themes.fill = GridBagConstraints.HORIZONTAL;
@ -84,7 +83,7 @@ public class ThemeCustomizationPanel extends SettingsPanel {
colorsPanel.setLayout(gbl_colorCustomizations);
Theme theme = settings.getCurrentTheme();
Theme theme = Settings.getInstance().getCurrentTheme();
buildCustomizeElements(theme);
GridBagConstraints gbc_colorsPanel = new GridBagConstraints();
@ -174,7 +173,7 @@ public class ThemeCustomizationPanel extends SettingsPanel {
buildCustomizeElement(theme, theme.getCellColor(), "Cells", "cellColor", 2);
buildCustomizeElement(theme, theme.getInteractableForegroundColor(), "Interactable Foreground", "interactableForegroundColor", 3);
buildCustomizeElement(theme, theme.getInteractableBackgroundColor(), "Interactable Background", "interactableBackgroundColor", 4);
buildCustomizeElement(theme, theme.getMessageTextColor(), "Messages Chat", "messageColorChat", 5);
buildCustomizeElement(theme, theme.getTextColor(), "Text Color", "textColor", 5);
buildCustomizeElement(theme, theme.getDateColor(), "Date Chat", "dateColorChat", 6);
buildCustomizeElement(theme, theme.getSelectionColor(), "Selection", "selectionColor", 7);
buildCustomizeElement(theme, theme.getTypingMessageColor(), "Typing Message", "typingMessageColor", 8);