added missing merge content,mnemonics support(ContextMenu)and Nullchecks

This commit is contained in:
delvh 2020-03-18 22:13:58 +01:00
parent a6b6a68a56
commit ad59fe0958
4 changed files with 91 additions and 65 deletions

View File

@ -46,15 +46,18 @@ public class ContextMenu extends JPopupMenu {
private Map<String, ActionListener> items = new HashMap<>(); private Map<String, ActionListener> items = new HashMap<>();
private Map<String, Icon> icons = new HashMap<>(); private Map<String, Icon> icons = new HashMap<>();
private Map<String, Integer> mnemonics = new HashMap<>();
private ButtonGroup radioButtonGroup = new ButtonGroup(); private ButtonGroup radioButtonGroup = new ButtonGroup();
private boolean built = false; private boolean built = false;
private boolean visible = false; private boolean visible = false;
/** /**
* @param parent the component which will call this
* {@link ContextMenu}
* @since Envoy v0.1-beta * @since Envoy v0.1-beta
*/ */
public ContextMenu() {} public ContextMenu(Component parent) { setInvoker(parent); }
/** /**
* @param label the string that a UI may use to display as a title * @param label the string that a UI may use to display as a title
@ -67,13 +70,18 @@ public class ContextMenu extends JPopupMenu {
* Only keys in here will have an Icon displayed. More * Only keys in here will have an Icon displayed. More
* precisely, all keys here not included in the first * precisely, all keys here not included in the first
* map will be thrown out. * 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 * @since Envoy v0.1-beta
*/ */
public ContextMenu(String label, Component parent, Map<String, ActionListener> itemsWithActions, Map<String, Icon> itemIcons) { public ContextMenu(String label, Component parent, Map<String, ActionListener> itemsWithActions, Map<String, Icon> itemIcons,
Map<String, Integer> itemMnemonics) {
super(label); super(label);
setInvoker(parent); setInvoker(parent);
this.items = (itemsWithActions != null) ? itemsWithActions : items; this.items = (itemsWithActions != null) ? itemsWithActions : items;
this.icons = (itemIcons != null) ? itemIcons : icons; this.icons = (itemIcons != null) ? itemIcons : icons;
this.mnemonics = (itemMnemonics != null) ? itemMnemonics : mnemonics;
} }
/** /**
@ -87,27 +95,20 @@ public class ContextMenu extends JPopupMenu {
public ContextMenu build() { public ContextMenu build() {
items.forEach((text, action) -> { items.forEach((text, action) -> {
// case radio button wanted // case radio button wanted
AbstractButton item;
if (text.startsWith(radioButtonMenuItem)) { if (text.startsWith(radioButtonMenuItem)) {
var item = new JRadioButtonMenuItem(text.substring(radioButtonMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null); item = new JRadioButtonMenuItem(text.substring(radioButtonMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
item.addActionListener(action);
radioButtonGroup.add(item); radioButtonGroup.add(item);
add(item);
// case check box wanted // case check box wanted
} else if (text.startsWith(checkboxMenuItem)) { } else if (text.startsWith(checkboxMenuItem))
var item = new JCheckBoxMenuItem(text.substring(checkboxMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null); item = new JCheckBoxMenuItem(text.substring(checkboxMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
item.addActionListener(action);
add(item);
// case sub-menu wanted // case sub-menu wanted
} else if (text.startsWith(subMenuItem)) { else if (text.startsWith(subMenuItem)) item = new JMenu(text.substring(subMenuItem.length()));
var 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); item.addActionListener(action);
if (mnemonics.containsKey(text)) item.setMnemonic(mnemonics.get(text));
add(item); add(item);
} else {
// normal JMenuItem wanted
var item = new JMenuItem(text, icons.containsKey(text) ? icons.get(text) : null);
item.addActionListener(action);
add(item);
}
}); });
getInvoker().addMouseListener(getShowingListener()); getInvoker().addMouseListener(getShowingListener());
built = true; built = true;
@ -136,10 +137,10 @@ public class ContextMenu extends JPopupMenu {
private void action(MouseEvent e) { private void action(MouseEvent e) {
if (!built) build(); if (!built) build();
if (e.isPopupTrigger()) { if (e.isPopupTrigger()) {
show(e.getComponent(), e.getX(), e.getY());
// hides the menu if already visible // hides the menu if already visible
visible = !visible; visible = !visible;
setVisible(visible); if (visible) show(e.getComponent(), e.getX(), e.getY());
else setVisible(false);
} }
} }
@ -155,6 +156,7 @@ public class ContextMenu extends JPopupMenu {
removeAll(); removeAll();
items = new HashMap<>(); items = new HashMap<>();
icons = new HashMap<>(); icons = new HashMap<>();
mnemonics = new HashMap<>();
} }
/** /**
@ -181,4 +183,19 @@ public class ContextMenu extends JPopupMenu {
* @since Envoy v0.1-beta * @since Envoy v0.1-beta
*/ */
public void setIcons(Map<String, Icon> icons) { this.icons = icons; } 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; }
} }

View File

@ -22,6 +22,7 @@ import envoy.client.event.MessageCreationEvent;
import envoy.client.event.ThemeChangeEvent; import envoy.client.event.ThemeChangeEvent;
import envoy.client.net.Client; import envoy.client.net.Client;
import envoy.client.net.WriteProxy; import envoy.client.net.WriteProxy;
import envoy.client.ui.ContextMenu;
import envoy.client.ui.Theme; import envoy.client.ui.Theme;
import envoy.client.ui.list.ComponentList; import envoy.client.ui.list.ComponentList;
import envoy.client.ui.list.ComponentListModel; import envoy.client.ui.list.ComponentListModel;
@ -29,6 +30,7 @@ import envoy.client.ui.primary.PrimaryButton;
import envoy.client.ui.primary.PrimaryScrollPane; import envoy.client.ui.primary.PrimaryScrollPane;
import envoy.client.ui.primary.PrimaryTextArea; import envoy.client.ui.primary.PrimaryTextArea;
import envoy.client.ui.renderer.ContactsSearchRenderer; import envoy.client.ui.renderer.ContactsSearchRenderer;
import envoy.client.ui.renderer.MessageListRenderer;
import envoy.client.ui.renderer.UserListRenderer; import envoy.client.ui.renderer.UserListRenderer;
import envoy.client.ui.settings.SettingsScreen; import envoy.client.ui.settings.SettingsScreen;
import envoy.data.Message; import envoy.data.Message;
@ -74,6 +76,7 @@ public class ChatWindow extends JFrame {
private JTextPane textPane = new JTextPane(); private JTextPane textPane = new JTextPane();
private PrimaryButton postButton = new PrimaryButton("Post"); private PrimaryButton postButton = new PrimaryButton("Post");
private PrimaryButton settingsButton = new PrimaryButton("Settings"); private PrimaryButton settingsButton = new PrimaryButton("Settings");
@SuppressWarnings("unused")
private JPopupMenu contextMenu; private JPopupMenu contextMenu;
// Contacts Header // Contacts Header
@ -107,6 +110,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")));
@ -121,12 +125,6 @@ public class ChatWindow extends JFrame {
contentPane.setLayout(gbl_contentPane); contentPane.setLayout(gbl_contentPane);
messageList.setBorder(new EmptyBorder(space, space, space, space)); messageList.setBorder(new EmptyBorder(space, space, space, space));
messageList.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.isPopupTrigger()) {
contextMenu = new JPopupMenu("message options");
Map<String, ActionListener> commands = new HashMap<>() { Map<String, ActionListener> commands = new HashMap<>() {
private static final long serialVersionUID = -2755235774946990126L; private static final long serialVersionUID = -2755235774946990126L;
@ -134,8 +132,7 @@ public class ChatWindow extends JFrame {
{ {
put("forward selected message", put("forward selected message",
evt -> forwardMessageToMultipleUsers(messageList.getSelected().get(0), evt -> forwardMessageToMultipleUsers(messageList.getSelected().get(0),
ContactsChooserDialog ContactsChooserDialog.showForwardingDialog("Forward selected message to", messageList.getSelected().get(0), client)));
.showForwardingDialog("Forward selected message to", messageList.getSelected().get(0), client)));
put("copy", evt -> { put("copy", evt -> {
// TODO should be enhanced to allow also copying of message attachments, // TODO should be enhanced to allow also copying of message attachments,
// especially pictures // especially pictures
@ -148,17 +145,17 @@ public class ChatWindow extends JFrame {
put("quote", evt -> {}); put("quote", evt -> {});
} }
}; };
commands.forEach((name, action) -> { var item = new JMenuItem(name); item.addActionListener(action); contextMenu.add(item); }); contextMenu = new ContextMenu(null, messageList, commands, null, null).build();
contextMenu.show(e.getComponent(), e.getX(), e.getY());
}
}
});
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);
@ -600,7 +597,7 @@ public class ChatWindow extends JFrame {
private void readCurrentChat() { private void readCurrentChat() {
try { try {
currentChat.read(writeProxy); currentChat.read(writeProxy);
messageList.synchronizeModel(); if (messageList.getRenderer() != null) messageList.synchronizeModel();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
logger.log(Level.WARNING, "Couldn't notify server about message status change", e); logger.log(Level.WARNING, "Couldn't notify server about message status change", e);
@ -644,6 +641,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

@ -31,6 +31,12 @@ public class ComponentList<E> extends JPanel {
private static final long serialVersionUID = 1759644503942876737L; private static final long serialVersionUID = 1759644503942876737L;
/**
* Sets the layout of this {@link ComponentList} to be a vertically oriented
* BoxLayout.
*
* @since Envoy v0.1-beta
*/
public ComponentList() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); } public ComponentList() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); }
/** /**
@ -74,7 +80,7 @@ public class ComponentList<E> extends JPanel {
// Synchronize with new model // Synchronize with new model
this.model = model; this.model = model;
if (model != null) this.model.setComponentList(this); if (model != null) this.model.setComponentList(this);
synchronizeModel(); if (renderer != null) synchronizeModel();
} }
/** /**
@ -84,10 +90,12 @@ public class ComponentList<E> extends JPanel {
* @since Envoy v0.3-alpha * @since Envoy v0.3-alpha
*/ */
public void synchronizeModel() { public void synchronizeModel() {
if (model != null && renderer != null) {
removeAll(); removeAll();
if (model != null) model.forEach(this::add); model.forEach(this::add);
revalidate(); revalidate();
} }
}
/** /**
* Adds an object to the list by rendering it with the current * Adds an object to the list by rendering it with the current
@ -112,10 +120,12 @@ public class ComponentList<E> extends JPanel {
clearSelections(); clearSelections();
currentSelections.add(index); currentSelections.add(index);
} }
if (renderer != null) {
final JComponent component = renderer.getListCellComponent(this, elem, isSelected); final JComponent component = renderer.getListCellComponent(this, elem, isSelected);
component.addMouseListener(getSelectionListener(index)); component.addMouseListener(getSelectionListener(index));
add(component, index); add(component, index);
} }
}
/** /**
* @param componentIndex the index of the list component to which the mouse * @param componentIndex the index of the list component to which the mouse

View File

@ -115,6 +115,6 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable {
*/ */
void setComponentList(ComponentList<E> componentList) { void setComponentList(ComponentList<E> componentList) {
this.componentList = componentList; this.componentList = componentList;
if (componentList != null) componentList.synchronizeModel(); if (componentList != null && componentList.getRenderer() != null) componentList.synchronizeModel();
} }
} }