Configs slight design tweak

- Updated the search bar to the new icon text field component.
- Added new on/off icons based on the material design style.
- Recoloured the background.
- Recoloured the plugin name labels.
- Replaced the config/toggle buttons for icon labels.
- Hid config button if no config was found, instead of disabling it.
- Left aligned the header title.
- Added new ComboBoxListRenderer to prevent substance's ugly coloring.
- Changed the panel title to "Configuration"
- Used deathbeam's new layout manager DynamicGridLayout
This commit is contained in:
Ruben Amendoeira
2018-04-22 19:34:27 +01:00
committed by Tomas Slusny
parent dcd241e3bf
commit 6ec0d60ec4
9 changed files with 406 additions and 63 deletions

View File

@@ -37,7 +37,6 @@ import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Comparator;
import java.util.Map;
@@ -61,6 +60,7 @@ import javax.swing.JTextField;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
@@ -75,16 +75,22 @@ import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginInstantiationException;
import net.runelite.client.plugins.PluginManager;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.DynamicGridLayout;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.ComboBoxListRenderer;
import net.runelite.client.ui.components.IconTextField;
@Slf4j
public class ConfigPanel extends PluginPanel
{
private static final int TEXT_FIELD_WIDTH = 7;
private static final int SPINNER_FIELD_WIDTH = 6;
private static BufferedImage CONFIG_ICON;
private static BufferedImage UNCHECK_ICON;
private static BufferedImage CHECK_ICON;
private static final ImageIcon CONFIG_ICON;
private static final ImageIcon ON_SWITCHER;
private static final ImageIcon OFF_SWITCHER;
private static final ImageIcon SEARCH;
static
{
@@ -92,14 +98,15 @@ public class ConfigPanel extends PluginPanel
{
synchronized (ImageIO.class)
{
CONFIG_ICON = ImageIO.read(ConfigPanel.class.getResourceAsStream("config_icon.png"));
UNCHECK_ICON = ImageIO.read(ConfigPanel.class.getResourceAsStream("disabled.png"));
CHECK_ICON = ImageIO.read(ConfigPanel.class.getResourceAsStream("enabled.png"));
CONFIG_ICON = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("config_edit_icon.png")));
ON_SWITCHER = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("switchers/on.png")));
OFF_SWITCHER = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("switchers/off.png")));
SEARCH = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("search.png")));
}
}
catch (IOException e)
{
log.warn("Failed to read icon", e);
throw new RuntimeException(e);
}
}
@@ -107,7 +114,7 @@ public class ConfigPanel extends PluginPanel
private final ConfigManager configManager;
private final ScheduledExecutorService executorService;
private final RuneLiteConfig runeLiteConfig;
private final JTextField searchBar = new JTextField();
private final IconTextField searchBar = new IconTextField();
private Map<String, JPanel> children = new TreeMap<>();
private int scrollBarPosition = 0;
@@ -119,6 +126,10 @@ public class ConfigPanel extends PluginPanel
this.executorService = executorService;
this.runeLiteConfig = runeLiteConfig;
searchBar.setIcon(SEARCH);
searchBar.setPreferredSize(new Dimension(100, 30));
searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR);
searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR);
searchBar.getDocument().addDocumentListener(new DocumentListener()
{
@Override
@@ -140,6 +151,10 @@ public class ConfigPanel extends PluginPanel
}
});
setBorder(new EmptyBorder(10, 10, 10, 10));
setLayout(new DynamicGridLayout(0, 1, 0, 5));
setBackground(ColorScheme.DARK_GRAY_COLOR);
rebuildPluginList();
openConfigList();
}
@@ -150,42 +165,54 @@ public class ConfigPanel extends PluginPanel
Map<String, JPanel> newChildren = new TreeMap<>();
pluginManager.getPlugins().stream()
.filter(plugin -> !plugin.getClass().getAnnotation(PluginDescriptor.class).hidden())
.sorted(Comparator.comparing(left -> left.getClass().getAnnotation(PluginDescriptor.class).name()))
.forEach(plugin ->
{
final Config pluginConfigProxy = pluginManager.getPluginConfigProxy(plugin);
final String pluginName = plugin.getClass().getAnnotation(PluginDescriptor.class).name();
.filter(plugin -> !plugin.getClass().getAnnotation(PluginDescriptor.class).hidden())
.sorted(Comparator.comparing(left -> left.getClass().getAnnotation(PluginDescriptor.class).name()))
.forEach(plugin ->
{
final Config pluginConfigProxy = pluginManager.getPluginConfigProxy(plugin);
final String pluginName = plugin.getClass().getAnnotation(PluginDescriptor.class).name();
final JPanel groupPanel = buildGroupPanel();
groupPanel.add(new JLabel(pluginName), BorderLayout.CENTER);
final JPanel groupPanel = buildGroupPanel();
final JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(1, 2, 3, 0));
groupPanel.add(buttonPanel, BorderLayout.LINE_END);
JLabel name = new JLabel(pluginName);
name.setForeground(Color.WHITE);
final JButton editConfigButton = buildConfigButton(pluginConfigProxy);
buttonPanel.add(editConfigButton);
groupPanel.add(name, BorderLayout.CENTER);
final JButton toggleButton = buildToggleButton(plugin);
buttonPanel.add(toggleButton);
final JPanel buttonPanel = new JPanel();
buttonPanel.setOpaque(false);
buttonPanel.setLayout(new GridLayout(1, 2));
groupPanel.add(buttonPanel, BorderLayout.LINE_END);
newChildren.put(pluginName, groupPanel);
});
final JLabel editConfigButton = buildConfigButton(pluginConfigProxy);
buttonPanel.add(editConfigButton);
final JLabel toggleButton = buildToggleButton(plugin);
toggleButton.setHorizontalAlignment(SwingConstants.RIGHT);
buttonPanel.add(toggleButton);
newChildren.put(pluginName, groupPanel);
});
final JPanel groupPanel = buildGroupPanel();
groupPanel.add(new JLabel("RuneLite"), BorderLayout.CENTER);
JLabel name = new JLabel("RuneLite");
name.setForeground(Color.WHITE);
groupPanel.add(name, BorderLayout.CENTER);
final JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(1, 2, 3, 0));
buttonPanel.setOpaque(false);
buttonPanel.setLayout(new GridLayout(1, 2));
groupPanel.add(buttonPanel, BorderLayout.LINE_END);
final JButton editConfigButton = buildConfigButton(runeLiteConfig);
final JLabel editConfigButton = buildConfigButton(runeLiteConfig);
buttonPanel.add(editConfigButton);
final JButton toggleButton = buildToggleButton(null);
final JLabel toggleButton = buildToggleButton(null);
toggleButton.setVisible(false);
buttonPanel.add(toggleButton);
newChildren.put("RuneLite", groupPanel);
children = newChildren;
@@ -197,15 +224,17 @@ public class ConfigPanel extends PluginPanel
// Create base panel for the config button and enabled/disabled button
final JPanel groupPanel = new JPanel();
groupPanel.setLayout(new BorderLayout(3, 0));
groupPanel.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH, 20));
groupPanel.setOpaque(false);
return groupPanel;
}
private JButton buildConfigButton(Config config)
private JLabel buildConfigButton(Config config)
{
// Create edit config button and disable it by default
final JButton editConfigButton = new JButton(new ImageIcon(CONFIG_ICON));
editConfigButton.setPreferredSize(new Dimension(32, 0));
editConfigButton.setEnabled(false);
final JLabel editConfigButton = new JLabel(CONFIG_ICON);
editConfigButton.setPreferredSize(new Dimension(25, 0));
editConfigButton.setVisible(false);
// If we have configuration proxy enable the button and add edit config listener
if (config != null)
@@ -215,8 +244,15 @@ public class ConfigPanel extends PluginPanel
if (!configEmpty)
{
editConfigButton.addActionListener(ae -> openGroupConfigPanel(config, configDescriptor, configManager));
editConfigButton.setEnabled(true);
editConfigButton.addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent mouseEvent)
{
openGroupConfigPanel(config, configDescriptor, configManager);
}
});
editConfigButton.setVisible(true);
editConfigButton.setToolTipText("Edit plugin configuration");
}
}
@@ -224,11 +260,11 @@ public class ConfigPanel extends PluginPanel
return editConfigButton;
}
private JButton buildToggleButton(Plugin plugin)
private JLabel buildToggleButton(Plugin plugin)
{
// Create enabling/disabling button
final JButton toggleButton = new JButton(new ImageIcon(CHECK_ICON));
toggleButton.setPreferredSize(new Dimension(32, 0));
final JLabel toggleButton = new JLabel(ON_SWITCHER);
toggleButton.setPreferredSize(new Dimension(25, 0));
if (plugin == null)
{
@@ -238,36 +274,43 @@ public class ConfigPanel extends PluginPanel
highlightButton(toggleButton, pluginManager.isPluginEnabled(plugin));
toggleButton.addActionListener(e -> executorService.submit(() ->
toggleButton.addMouseListener(new MouseAdapter()
{
final boolean enabled = pluginManager.isPluginEnabled(plugin);
pluginManager.setPluginEnabled(plugin, !enabled);
try
@Override
public void mousePressed(MouseEvent mouseEvent)
{
if (enabled)
executorService.submit(() ->
{
pluginManager.stopPlugin(plugin);
}
else
{
pluginManager.startPlugin(plugin);
}
}
catch (PluginInstantiationException ex)
{
log.warn("Error during starting/stopping plugin {}", plugin.getClass().getSimpleName(), ex);
}
final boolean enabled = pluginManager.isPluginEnabled(plugin);
pluginManager.setPluginEnabled(plugin, !enabled);
highlightButton(toggleButton, !enabled);
}));
try
{
if (enabled)
{
pluginManager.stopPlugin(plugin);
}
else
{
pluginManager.startPlugin(plugin);
}
}
catch (PluginInstantiationException ex)
{
log.warn("Error during starting/stopping plugin {}", plugin.getClass().getSimpleName(), ex);
}
highlightButton(toggleButton, !enabled);
});
}
});
return toggleButton;
}
private void highlightButton(JButton button, boolean enabled)
private void highlightButton(JLabel button, boolean enabled)
{
button.setIcon(enabled ? new ImageIcon(CHECK_ICON) : new ImageIcon(UNCHECK_ICON));
button.setIcon(enabled ? ON_SWITCHER : OFF_SWITCHER);
button.setToolTipText(enabled ? "Disable plugin" : "Enable plugin");
}
@@ -301,7 +344,11 @@ public class ConfigPanel extends PluginPanel
private void openConfigList()
{
removeAll();
add(new JLabel("Plugin Configuration", SwingConstants.CENTER));
JLabel title = new JLabel("Configuration", SwingConstants.LEFT);
title.setForeground(Color.WHITE);
add(title);
add(searchBar);
onSearchBarChanged();
@@ -331,6 +378,7 @@ public class ConfigPanel extends PluginPanel
if (component instanceof JCheckBox)
{
JCheckBox checkbox = (JCheckBox) component;
checkbox.setOpaque(false);
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), "" + checkbox.isSelected());
}
@@ -355,6 +403,8 @@ public class ConfigPanel extends PluginPanel
if (component instanceof JComboBox)
{
JComboBox jComboBox = (JComboBox) component;
jComboBox.setRenderer(new ComboBoxListRenderer());
jComboBox.setForeground(Color.WHITE);
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), ((Enum) jComboBox.getSelectedItem()).name());
}
}
@@ -376,6 +426,7 @@ public class ConfigPanel extends PluginPanel
}
JPanel item = new JPanel();
item.setOpaque(false);
item.setLayout(new BorderLayout());
name = cid.getItem().name();
JLabel configEntryName = new JLabel(name);
@@ -385,6 +436,8 @@ public class ConfigPanel extends PluginPanel
if (cid.getType() == boolean.class)
{
JCheckBox checkbox = new JCheckBox();
checkbox.setOpaque(false);
checkbox.setBackground(ColorScheme.LIGHT_GRAY_COLOR);
checkbox.setSelected(Boolean.parseBoolean(configManager.getConfiguration(cd.getGroup().keyName(), cid.getItem().keyName())));
checkbox.addActionListener(ae -> changeConfiguration(config, checkbox, cd, cid));
@@ -462,6 +515,7 @@ public class ConfigPanel extends PluginPanel
if (cid.getType() == Dimension.class)
{
JPanel dimensionPanel = new JPanel();
dimensionPanel.setOpaque(false);
dimensionPanel.setLayout(new BorderLayout());
String str = configManager.getConfiguration(cd.getGroup().keyName(), cid.getItem().keyName());
@@ -482,7 +536,7 @@ public class ConfigPanel extends PluginPanel
heightSpinnerTextField.setColumns(4);
ChangeListener listener = e ->
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), widthSpinner.getValue() + "x" + heightSpinner.getValue());
configManager.setConfiguration(cd.getGroup().keyName(), cid.getItem().keyName(), widthSpinner.getValue() + "x" + heightSpinner.getValue());
widthSpinner.addChangeListener(listener);
heightSpinner.addChangeListener(listener);
@@ -498,6 +552,9 @@ public class ConfigPanel extends PluginPanel
{
Class<? extends Enum> type = (Class<? extends Enum>) cid.getType();
JComboBox box = new JComboBox(type.getEnumConstants());
box.setPreferredSize(new Dimension(box.getPreferredSize().width, 25));
box.setRenderer(new ComboBoxListRenderer());
box.setForeground(Color.WHITE);
box.setFocusable(false);
box.setPrototypeDisplayValue("XXXXXXXX"); //sorry but this is the way to keep the size of the combobox in check.
try
@@ -541,4 +598,4 @@ public class ConfigPanel extends PluginPanel
revalidate();
getScrollPane().getVerticalScrollBar().setValue(0);
}
}
}

View File

@@ -0,0 +1,215 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.ui;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Insets;
import java.util.function.Function;
/**
* Grid layout implementation with support for cells with unequal size.
*
* See https://www.javaworld.com/article/2077486/core-java/java-tip-121--flex-your-grid-layout.html
*/
public class DynamicGridLayout extends GridLayout
{
public DynamicGridLayout()
{
this(1, 0, 0, 0);
}
public DynamicGridLayout(int rows, int cols)
{
this(rows, cols, 0, 0);
}
public DynamicGridLayout(int rows, int cols, int hgap, int vgap)
{
super(rows, cols, hgap, vgap);
}
@Override
public Dimension preferredLayoutSize(Container parent)
{
synchronized (parent.getTreeLock())
{
return calculateSize(parent, Component::getPreferredSize);
}
}
@Override
public Dimension minimumLayoutSize(Container parent)
{
synchronized (parent.getTreeLock())
{
return calculateSize(parent, Component::getMinimumSize);
}
}
@Override
public void layoutContainer(Container parent)
{
synchronized (parent.getTreeLock())
{
final Insets insets = parent.getInsets();
final int ncomponents = parent.getComponentCount();
int nrows = getRows();
int ncols = getColumns();
if (ncomponents == 0)
{
return;
}
if (nrows > 0)
{
ncols = (ncomponents + nrows - 1) / nrows;
}
else
{
nrows = (ncomponents + ncols - 1) / ncols;
}
final int hgap = getHgap();
final int vgap = getVgap();
// scaling factors
final Dimension pd = preferredLayoutSize(parent);
final double sw = (1.0 * parent.getWidth()) / pd.width;
final double sh = (1.0 * parent.getHeight()) / pd.height;
final int[] w = new int[ncols];
final int[] h = new int[nrows];
// calculate dimensions for all components + apply scaling
for (int i = 0; i < ncomponents; i++)
{
final int r = i / ncols;
final int c = i % ncols;
final Component comp = parent.getComponent(i);
final Dimension d = comp.getPreferredSize();
d.width = (int) (sw * d.width);
d.height = (int) (sh * d.height);
if (w[c] < d.width)
{
w[c] = d.width;
}
if (h[r] < d.height)
{
h[r] = d.height;
}
}
// Apply new bounds to all child components
for (int c = 0, x = insets.left; c < ncols; c++)
{
for (int r = 0, y = insets.top; r < nrows; r++)
{
int i = r * ncols + c;
if (i < ncomponents)
{
parent.getComponent(i).setBounds(x, y, w[c], h[r]);
}
y += h[r] + vgap;
}
x += w[c] + hgap;
}
}
}
/**
* Calculate outer size of the layout based on it's children and sizer
* @param parent parent component
* @param sizer functioning returning dimension of the child component
* @return outer size
*/
private Dimension calculateSize(final Container parent, final Function<Component, Dimension> sizer)
{
final int ncomponents = parent.getComponentCount();
int nrows = getRows();
int ncols = getColumns();
if (nrows > 0)
{
ncols = (ncomponents + nrows - 1) / nrows;
}
else
{
nrows = (ncomponents + ncols - 1) / ncols;
}
final int[] w = new int[ncols];
final int[] h = new int[nrows];
// Calculate dimensions for all components
for (int i = 0; i < ncomponents; i++)
{
final int r = i / ncols;
final int c = i % ncols;
final Component comp = parent.getComponent(i);
final Dimension d = sizer.apply(comp);
if (w[c] < d.width)
{
w[c] = d.width;
}
if (h[r] < d.height)
{
h[r] = d.height;
}
}
// Calculate total width and height of the layout
int nw = 0;
for (int j = 0; j < ncols; j++)
{
nw += w[j];
}
int nh = 0;
for (int i = 0; i < nrows; i++)
{
nh += h[i];
}
final Insets insets = parent.getInsets();
// Apply insets and horizontal and vertical gap to layout
return new Dimension(
insets.left + insets.right + nw + (ncols - 1) * getHgap(),
insets.top + insets.bottom + nh + (nrows - 1) * getVgap());
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2017, Psikoi <https://github.com/psikoi>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.ui.components;
import java.awt.Color;
import java.awt.Component;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import javax.swing.border.EmptyBorder;
import net.runelite.client.ui.ColorScheme;
/**
* A custom list renderer to avoid substance's weird coloring.
* Substance was making selected items' foreground color black, this
* was very hard to see in the dark gray background, this makes the selected
* item white and adds some padding to the elements for more readable list.
*/
public final class ComboBoxListRenderer extends JLabel implements ListCellRenderer
{
@Override
public Component getListCellRendererComponent(JList list, Object o, int index, boolean isSelected, boolean cellHasFocus)
{
if (isSelected)
{
setBackground(ColorScheme.DARK_GRAY_COLOR);
setForeground(Color.WHITE);
}
else
{
setBackground(list.getBackground());
setForeground(ColorScheme.LIGHT_GRAY_COLOR);
}
setBorder(new EmptyBorder(5, 5, 5, 0));
String text = (String) o.toString();
setText(text);
return this;
}
}

View File

@@ -37,6 +37,7 @@ import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.text.Document;
import net.runelite.client.ui.ColorScheme;
/**
@@ -160,4 +161,9 @@ public class IconTextField extends JPanel
}
}
public Document getDocument()
{
return textField.getDocument();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB