Browse Source
Conflicts: designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.javafeature/big-screen
pengda
3 years ago
27 changed files with 942 additions and 103 deletions
@ -0,0 +1,120 @@
|
||||
package com.fr.design.login.message; |
||||
|
||||
import com.fr.concurrent.NamedThreadFactory; |
||||
import com.fr.design.DesignerEnvManager; |
||||
import com.fr.design.dialog.NotificationDialog; |
||||
import com.fr.design.dialog.NotificationDialogAction; |
||||
import com.fr.design.event.DesignerOpenedListener; |
||||
import com.fr.design.login.utils.DesignerLoginUtils; |
||||
import com.fr.design.mainframe.DesignerContext; |
||||
import com.fr.design.utils.BrowseUtils; |
||||
import com.fr.general.CloudCenter; |
||||
import com.fr.general.http.HttpToolbox; |
||||
import com.fr.json.JSON; |
||||
import com.fr.json.JSONFactory; |
||||
import com.fr.json.JSONObject; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.stable.StringUtils; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.concurrent.Executors; |
||||
import java.util.concurrent.ScheduledExecutorService; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
/** |
||||
* @author Lanlan |
||||
* @version 10.0 |
||||
* Created by Lanlan on 2021/6/11 |
||||
*/ |
||||
public class DesignerMessageHelper { |
||||
|
||||
private static final long DELAY = 7L; |
||||
private static final String STATUS = "status"; |
||||
private static final String DATA = "data"; |
||||
private static final String SUCCESS = "success"; |
||||
private static final String MESSAGE_ID = "messageId"; |
||||
private static final String TITLE = "title"; |
||||
private static final String BODY = "body"; |
||||
private static final String JUMP_TYPE = "jumpType"; |
||||
private static final String JUMP_TO = "jumpTo"; |
||||
|
||||
private static DesignerMessageHelper instance; |
||||
|
||||
private DesignerMessageHelper() { |
||||
} |
||||
|
||||
public static DesignerMessageHelper getInstance() { |
||||
if (instance == null) { |
||||
instance = new DesignerMessageHelper(); |
||||
} |
||||
return instance; |
||||
} |
||||
|
||||
public void prepareShowMessage() { |
||||
DesignerContext.getDesignerFrame().addDesignerOpenedListener(new DesignerOpenedListener() { |
||||
@Override |
||||
public void designerOpened() { |
||||
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("DesignerMessageHelper")); |
||||
service.schedule(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
try { |
||||
pullLatestMessageAndShow(); |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
} |
||||
}, DELAY, TimeUnit.MINUTES); |
||||
service.shutdown(); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
private void pullLatestMessageAndShow() throws Exception { |
||||
String url = CloudCenter.getInstance().acquireUrlByKind("designer.message.push", "https://market.fanruan.com/api/v1/message/designer"); |
||||
Map<String, String> params = new HashMap<>(); |
||||
params.put("designerId", DesignerEnvManager.getEnvManager().getUUID()); |
||||
String result = HttpToolbox.post(url, params); |
||||
JSONObject response = JSONFactory.createJSON(JSON.OBJECT, result); |
||||
String status = response.optString(STATUS); |
||||
if (SUCCESS.equals(status)) { |
||||
JSONObject data = response.optJSONObject(DATA); |
||||
String messageId = data.optString(MESSAGE_ID); |
||||
String title = data.optString(TITLE); |
||||
String body = data.optString(BODY); |
||||
int jumpType = data.optInt(JUMP_TYPE); |
||||
String jumpTo = data.optString(JUMP_TO); |
||||
if (StringUtils.isNotEmpty(messageId) && StringUtils.isNotEmpty(title) && StringUtils.isNotEmpty(body) && jumpType > 0 && StringUtils.isNotEmpty(jumpTo)) { |
||||
NotificationJumpType notificationJumpType = NotificationJumpType.valueOf(jumpType); |
||||
if (notificationJumpType == NotificationJumpType.WEB_URL) { |
||||
NotificationDialog.Builder() |
||||
.owner(DesignerContext.getDesignerFrame()) |
||||
.title(title) |
||||
.modal(true) |
||||
.messageType(NotificationDialog.NEW_MESSAGE) |
||||
.message(body) |
||||
.notificationDialogAction(new NotificationDialogAction() { |
||||
@Override |
||||
public void doClick() { |
||||
String ssoUrl = DesignerLoginUtils.generateDesignerSSOUrl(jumpTo); |
||||
BrowseUtils.browser(ssoUrl); |
||||
} |
||||
}) |
||||
.build() |
||||
.setVisible(true); |
||||
} else if (notificationJumpType == NotificationJumpType.DESIGNER_MODULE) { |
||||
DesignerModuleClickType designerModuleClickType = DesignerModuleClickType.valueOf(jumpTo); |
||||
NotificationDialog.Builder() |
||||
.owner(DesignerContext.getDesignerFrame()) |
||||
.title(title) |
||||
.modal(true) |
||||
.messageType(NotificationDialog.NEW_MESSAGE) |
||||
.message(body) |
||||
.notificationDialogAction(designerModuleClickType.getAction()) |
||||
.build() |
||||
.setVisible(true); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,69 @@
|
||||
package com.fr.design.login.message; |
||||
|
||||
import com.fr.config.ServerPreferenceConfig; |
||||
import com.fr.design.dialog.NotificationDialogAction; |
||||
import com.fr.design.extra.WebViewDlgHelper; |
||||
import com.fr.design.os.impl.SupportOSImpl; |
||||
import com.fr.design.upm.UpmFinder; |
||||
import com.fr.design.utils.DesignUtils; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.stable.os.Arch; |
||||
import com.fr.stable.os.OperatingSystem; |
||||
|
||||
/** |
||||
* @author Lanlan |
||||
* @version 10.0 |
||||
* Created by Lanlan on 2021/6/11 |
||||
*/ |
||||
public enum DesignerModuleClickType { |
||||
PLUGIN("PLUGIN", new NotificationDialogAction() { |
||||
@Override |
||||
public void doClick() { |
||||
try { |
||||
if (Arch.getArch() == Arch.ARM || OperatingSystem.isLinux() || SupportOSImpl.MACOS_WEB_PLUGIN_MANAGEMENT.support()) { |
||||
DesignUtils.visitEnvServerByParameters("#management/plugin", null, null); |
||||
return; |
||||
} |
||||
if (ServerPreferenceConfig.getInstance().isUseOptimizedUPM() || SupportOSImpl.MACOS_NEW_PLUGIN_MANAGEMENT.support()) { |
||||
UpmFinder.showUPMDialog(); |
||||
} else { |
||||
WebViewDlgHelper.createPluginDialog(); |
||||
} |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
} |
||||
}), |
||||
REUSE("REUSE", new NotificationDialogAction() { |
||||
@Override |
||||
public void doClick() { |
||||
try { |
||||
// TODO
|
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
} |
||||
}), |
||||
UNKNOWN(StringUtils.EMPTY, new NotificationDialogAction() { |
||||
@Override |
||||
public void doClick() { |
||||
} |
||||
}); |
||||
|
||||
private String jumpTo; |
||||
private NotificationDialogAction action; |
||||
|
||||
DesignerModuleClickType(String jumpTo, NotificationDialogAction action) { |
||||
this.jumpTo = jumpTo; |
||||
this.action = action; |
||||
} |
||||
|
||||
public String getJumpTo() { |
||||
return jumpTo; |
||||
} |
||||
|
||||
public NotificationDialogAction getAction() { |
||||
return action; |
||||
} |
||||
} |
@ -0,0 +1,31 @@
|
||||
package com.fr.design.login.message; |
||||
|
||||
/** |
||||
* @author Lanlan |
||||
* @version 10.0 |
||||
* Created by Lanlan on 2021/6/11 |
||||
*/ |
||||
public enum NotificationJumpType { |
||||
WEB_URL(1), |
||||
DESIGNER_MODULE(2), |
||||
UNKNOWN(-1); |
||||
|
||||
private int jumpType; |
||||
|
||||
NotificationJumpType(int jumpType) { |
||||
this.jumpType = jumpType; |
||||
} |
||||
|
||||
public int getJumpType() { |
||||
return jumpType; |
||||
} |
||||
|
||||
public static NotificationJumpType valueOf(int jumpType) { |
||||
for(NotificationJumpType value : NotificationJumpType.values()) { |
||||
if(value.getJumpType() == jumpType) { |
||||
return value; |
||||
} |
||||
} |
||||
return UNKNOWN; |
||||
} |
||||
} |
@ -0,0 +1,40 @@
|
||||
package com.fr.design.notification; |
||||
|
||||
import com.fr.design.dialog.NotificationDialogAction; |
||||
|
||||
public class Notification { |
||||
private int type; |
||||
private String message; |
||||
private NotificationDialogAction notificationDialogAction; |
||||
public static final int ERROR_MESSAGE = 0; |
||||
public static final int NEW_MESSAGE = 1; |
||||
public static final int WARNING_MESSAGE = 2; |
||||
public Notification(int type,String message,NotificationDialogAction notificationDialogAction){ |
||||
this.type = type; |
||||
this.message = message; |
||||
this.notificationDialogAction = notificationDialogAction; |
||||
} |
||||
|
||||
public Notification(String message){ |
||||
this.type = WARNING_MESSAGE; |
||||
this.message = message; |
||||
this.notificationDialogAction = new NotificationDialogAction() { |
||||
@Override |
||||
public void doClick() { |
||||
//do nothing
|
||||
} |
||||
}; |
||||
} |
||||
|
||||
public NotificationDialogAction getNotificationDialogAction() { |
||||
return notificationDialogAction; |
||||
} |
||||
|
||||
public int getType(){ |
||||
return type; |
||||
} |
||||
|
||||
public String getMessage(){ |
||||
return message; |
||||
} |
||||
} |
@ -0,0 +1,42 @@
|
||||
package com.fr.design.notification; |
||||
|
||||
|
||||
import com.fr.design.notification.ui.NotificationCenterPane; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
public class NotificationCenter { |
||||
private static NotificationCenter notificationCenter = new NotificationCenter(); |
||||
private List<Notification> notifications; |
||||
private NotificationCenter(){ |
||||
notifications = new ArrayList<>(); |
||||
} |
||||
|
||||
public static NotificationCenter getInstance(){ |
||||
return notificationCenter; |
||||
} |
||||
|
||||
public void addNotification(Notification message){ |
||||
notifications.add(message); |
||||
NotificationCenterPane.getNotificationCenterPane().refreshButton(); |
||||
} |
||||
|
||||
public int getNotificationsCount(){ |
||||
return notifications.size(); |
||||
} |
||||
|
||||
public void removeNotification(int index){ |
||||
notifications.remove(index); |
||||
NotificationCenterPane.getNotificationCenterPane().refreshButton(); |
||||
} |
||||
|
||||
public Notification getNotification(int index){ |
||||
return notifications.get(index); |
||||
} |
||||
|
||||
public void clearAllNotifications(){ |
||||
notifications.clear(); |
||||
NotificationCenterPane.getNotificationCenterPane().refreshButton(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,222 @@
|
||||
package com.fr.design.notification.ui; |
||||
|
||||
import com.fr.design.dialog.NotificationDialogAction; |
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.layout.FRGUIPaneFactory; |
||||
import com.fr.design.notification.Notification; |
||||
import com.fr.design.notification.NotificationCenter; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Color; |
||||
import java.awt.Cursor; |
||||
import java.awt.Dimension; |
||||
import java.awt.Frame; |
||||
import java.awt.Toolkit; |
||||
import java.awt.Window; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
import java.awt.event.MouseListener; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.Icon; |
||||
import javax.swing.JDialog; |
||||
import javax.swing.JPanel; |
||||
|
||||
import javax.swing.UIManager; |
||||
|
||||
public class NotificationCenterDialog extends JDialog { |
||||
|
||||
private ArrayList<NotificationPane> notificationNeedShow; |
||||
private JPanel centerPanel; |
||||
private UILabel deleteLabel; |
||||
private static final int NOTIFICATIONCOUNT = 5; |
||||
|
||||
public NotificationCenterDialog(Frame parent) { |
||||
super(parent, true); |
||||
setTitle(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Notification")); |
||||
setResizable(false); |
||||
notificationNeedShow = new ArrayList<>(); |
||||
initComponents(); |
||||
} |
||||
|
||||
public void initComponents() { |
||||
centerPanel = FRGUIPaneFactory.createNColumnGridInnerContainer_S_Pane(1); |
||||
addNotification(); |
||||
deleteLabel = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Clear_Notifications") + "(" + NotificationCenter.getInstance().getNotificationsCount() + ")"); |
||||
deleteLabel.setForeground(Color.BLUE); |
||||
|
||||
deleteLabel.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
NotificationCenter.getInstance().clearAllNotifications(); |
||||
centerPanel.removeAll(); |
||||
addNotification(); |
||||
pack(); |
||||
deleteLabel.setText(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Clear_Notifications") + "(" + NotificationCenter.getInstance().getNotificationsCount() + ")"); |
||||
if (notificationNeedShow.size() == 0) { |
||||
hideDialog(); |
||||
} |
||||
} |
||||
}); |
||||
JPanel deletePane = new JPanel(FRGUIPaneFactory.createCenterFlowLayout()); |
||||
deletePane.add(deleteLabel); |
||||
deletePane.setPreferredSize(new Dimension(240, 30)); |
||||
add(centerPanel, BorderLayout.CENTER); |
||||
add(deletePane, BorderLayout.SOUTH); |
||||
pack(); |
||||
centerWindow(); |
||||
} |
||||
|
||||
private void hideDialog() { |
||||
this.dispose(); |
||||
} |
||||
|
||||
private void addNotification() { |
||||
notificationNeedShow.clear(); |
||||
|
||||
int size = NotificationCenter.getInstance().getNotificationsCount(); |
||||
for (int i = NOTIFICATIONCOUNT; i > 0; i--) { |
||||
int j = size - i; |
||||
if (j >= 0) { |
||||
Notification notification = NotificationCenter.getInstance().getNotification(j); |
||||
NotificationPane notificationPane = new NotificationPane(notification.getType(), notification.getMessage(), i, notification.getNotificationDialogAction()); |
||||
notificationNeedShow.add(notificationPane); |
||||
} |
||||
} |
||||
size = notificationNeedShow.size(); |
||||
for (int i = size - 1; i >= 0; i--) { |
||||
centerPanel.add(notificationNeedShow.get(i)); |
||||
} |
||||
} |
||||
|
||||
private void centerWindow() { |
||||
Window win = this; |
||||
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); |
||||
|
||||
Dimension winSize = win.getSize(); |
||||
|
||||
if (winSize.height > screenSize.height) { |
||||
winSize.height = screenSize.height; |
||||
} |
||||
if (winSize.width > screenSize.width) { |
||||
winSize.width = screenSize.width; |
||||
} |
||||
//这里设置位置:水平居中,竖直偏上
|
||||
win.setLocation(screenSize.width - winSize.width - 90, 50); |
||||
} |
||||
|
||||
/** |
||||
* 一条消息面板 |
||||
*/ |
||||
|
||||
class NotificationPane extends JPanel { |
||||
private int index; |
||||
private UILabel messageLabel; |
||||
private UILabel messageIcon; |
||||
private NotificationDialogAction notificationDialogAction; |
||||
|
||||
public NotificationPane(int type, String message, int index, NotificationDialogAction notificationDialogAction) { |
||||
this.index = index; |
||||
this.notificationDialogAction = notificationDialogAction; |
||||
this.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||
setTypeIcon(getIconForType(type)); |
||||
setNotification(message); |
||||
setDeleteIcon(); |
||||
} |
||||
|
||||
public void setTypeIcon(Icon icon) { |
||||
messageIcon = new UILabel(icon); |
||||
messageIcon.addMouseListener(messageAndIconListener); |
||||
JPanel messageIconPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
messageIconPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 8)); |
||||
messageIconPanel.add(messageIcon); |
||||
add(messageIconPanel, BorderLayout.WEST); |
||||
} |
||||
|
||||
public void setDeleteIcon() { |
||||
UILabel deleteIcon = new UILabel(UIManager.getIcon("OptionPane.deleteIcon")); |
||||
JPanel deleteIconPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
deleteIconPane.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 7)); |
||||
deleteIconPane.add(deleteIcon); |
||||
deleteIconPane.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
NotificationCenter.getInstance().removeNotification(NotificationCenter.getInstance().getNotificationsCount() - index); |
||||
centerPanel.removeAll(); |
||||
addNotification(); |
||||
deleteLabel.setText(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Clear_Notifications") + "(" + NotificationCenter.getInstance().getNotificationsCount() + ")"); |
||||
pack(); |
||||
if (notificationNeedShow.size() == 0) { |
||||
hideDialog(); |
||||
} |
||||
NotificationCenterPane.getNotificationCenterPane().refreshButton(); |
||||
} |
||||
|
||||
@Override |
||||
public void mouseEntered(MouseEvent e) { |
||||
deleteIcon.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void mouseExited(MouseEvent e) { |
||||
deleteIcon.setCursor(Cursor.getDefaultCursor()); |
||||
} |
||||
}); |
||||
add(deleteIconPane, BorderLayout.EAST); |
||||
} |
||||
|
||||
public int getIndex() { |
||||
return index; |
||||
} |
||||
|
||||
public void setNotification(String message) { |
||||
messageLabel = new UILabel("<html>" + message + "</html>"); |
||||
messageLabel.addMouseListener(messageAndIconListener); |
||||
JPanel labelPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
labelPanel.add(messageLabel); |
||||
this.add(labelPanel, BorderLayout.CENTER); |
||||
} |
||||
|
||||
protected Icon getIconForType(int messageType) { |
||||
String propertyName; |
||||
switch (messageType) { |
||||
case 0: |
||||
propertyName = "OptionPane.circularErrorIcon"; |
||||
break; |
||||
case 1: |
||||
propertyName = "OptionPane.newMessageIcon"; |
||||
break; |
||||
case 2: |
||||
propertyName = "OptionPane.circularWarningIcon"; |
||||
break; |
||||
default: |
||||
return null; |
||||
} |
||||
return UIManager.getIcon(propertyName); |
||||
} |
||||
|
||||
private MouseListener messageAndIconListener = new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
hideDialog(); |
||||
notificationDialogAction.doClick(); |
||||
} |
||||
|
||||
@Override |
||||
public void mouseEntered(MouseEvent e) { |
||||
messageLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); |
||||
messageIcon.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); |
||||
messageLabel.setForeground(new Color(250, 170, 57)); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void mouseExited(MouseEvent e) { |
||||
messageLabel.setCursor(Cursor.getDefaultCursor()); |
||||
messageIcon.setCursor(Cursor.getDefaultCursor()); |
||||
messageLabel.setForeground(Color.BLACK); |
||||
} |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,54 @@
|
||||
package com.fr.design.notification.ui; |
||||
|
||||
import com.fr.design.constants.UIConstants; |
||||
import com.fr.design.dialog.BasicPane; |
||||
import com.fr.design.gui.ibutton.UIButton; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.mainframe.DesignerContext; |
||||
import com.fr.design.notification.NotificationCenter; |
||||
import com.fr.general.IOUtils; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Dimension; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
|
||||
public class NotificationCenterPane extends BasicPane { |
||||
private static NotificationCenterPane notificationCenterPane = new NotificationCenterPane(); |
||||
private static UIButton notificationCenterButton; |
||||
|
||||
private NotificationCenterPane() { |
||||
setPreferredSize(new Dimension(24, 24)); |
||||
setLayout(new BorderLayout()); |
||||
notificationCenterButton = new UIButton(); |
||||
notificationCenterButton.setIcon(IOUtils.readIcon("/com/fr/design/mainframe/notificationcenter/notificationCenter.png")); |
||||
notificationCenterButton.setToolTipText(Toolkit.i18nText("Fine-Design_Basic_Show_Notification")); |
||||
notificationCenterButton.set4ToolbarButton(); |
||||
notificationCenterButton.setRolloverEnabled(false); |
||||
this.add(notificationCenterButton); |
||||
notificationCenterButton.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
NotificationCenterDialog notificationCenterDialog = new NotificationCenterDialog(DesignerContext.getDesignerFrame()); |
||||
notificationCenterDialog.setVisible(true); |
||||
} |
||||
}); |
||||
this.setBackground(UIConstants.TEMPLATE_TAB_PANE_BACKGROUND); |
||||
} |
||||
|
||||
public static NotificationCenterPane getNotificationCenterPane() { |
||||
return notificationCenterPane; |
||||
} |
||||
|
||||
public void refreshButton() { |
||||
if (NotificationCenter.getInstance().getNotificationsCount() > 0) { |
||||
notificationCenterButton.setIcon(IOUtils.readIcon("/com/fr/design/mainframe/notificationcenter/notificationCenterDot.png")); |
||||
} else { |
||||
notificationCenterButton.setIcon(IOUtils.readIcon("/com/fr/design/mainframe/notificationcenter/notificationCenter.png")); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected String title4PopupWindow() { |
||||
return "NotificationCenter"; |
||||
} |
||||
} |
After Width: | Height: | Size: 326 B |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 543 B |
Before Width: | Height: | Size: 299 B After Width: | Height: | Size: 546 B |
After Width: | Height: | Size: 347 B |
Loading…
Reference in new issue