From fc3ac09dad86d5256a64dff905d6a4fd6fca29cb Mon Sep 17 00:00:00 2001 From: thc202 Date: Mon, 24 Feb 2020 14:44:03 +0000 Subject: [PATCH] Add FlatLaf L&F Add FlatLaf dependency. Change ExtensionUiUtils to install the L&F provided by FlatLaf. Save the class of the look and feel to set it at start up (to not require installing the L&Fs before start up). Part of #5542 - Add a dark mode Signed-off-by: thc202 --- LEGALNOTICE.md | 1 + .../extension/option/OptionsParamView.java | 62 +++++++++++++++++-- .../extension/option/OptionsViewPanel.java | 60 +++++++++++++----- .../java/org/zaproxy/zap/GuiBootstrap.java | 3 +- .../extension/uiutils/ExtensionUiUtils.java | 14 +++++ zap/zap.gradle.kts | 1 + 6 files changed, 120 insertions(+), 21 deletions(-) diff --git a/LEGALNOTICE.md b/LEGALNOTICE.md index 8024ef01628..1b6553c642a 100644 --- a/LEGALNOTICE.md +++ b/LEGALNOTICE.md @@ -50,6 +50,7 @@ and subject to their respective licenses. | commons-validator-1.6.jar | Apache 2.0 | | delight-nashorn-sandbox-0.1.26.jar | Apache 2.0 | | ezmorph-1.0.6.jar | Apache 2.0 | +| flatlaf-0.27.jar | Apache 2.0 | | harlib-1.1.2.jar | Apache 2.0 | | hsqldb-2.5.0.jar | BSD | | ice4j-1.0.jar | Apache 2.0 | diff --git a/zap/src/main/java/org/parosproxy/paros/extension/option/OptionsParamView.java b/zap/src/main/java/org/parosproxy/paros/extension/option/OptionsParamView.java index f82043fa41a..941f3dec544 100644 --- a/zap/src/main/java/org/parosproxy/paros/extension/option/OptionsParamView.java +++ b/zap/src/main/java/org/parosproxy/paros/extension/option/OptionsParamView.java @@ -42,11 +42,14 @@ // ZAP: 2018/06/11 Added options for Work Panels Font. // ZAP: 2019/06/01 Normalise line endings. // ZAP: 2019/06/05 Normalise format/style. +// ZAP: 2020/02/24 Persist the class of the selected look and feel. package org.parosproxy.paros.extension.option; import java.util.EnumMap; import java.util.Locale; import java.util.Map; +import java.util.Objects; +import javax.swing.UIManager.LookAndFeelInfo; import org.parosproxy.paros.Constant; import org.parosproxy.paros.common.AbstractParam; import org.parosproxy.paros.control.Control.Mode; @@ -97,6 +100,14 @@ public class OptionsParamView extends AbstractParam { public static final String SCALE_IMAGES = "view.scaleImages"; public static final String SHOW_DEV_WARNING = "view.showDevWarning"; public static final String LOOK_AND_FEEL = "view.lookAndFeel"; + public static final String LOOK_AND_FEEL_CLASS = "view.lookAndFeelClass"; + + /** + * The default look and feel, has empty name and class. + * + * @since TODO add version + */ + public static final LookAndFeelInfo DEFAULT_LOOK_AND_FEEL = new LookAndFeelInfo("", ""); private static final String CONFIRM_REMOVE_PROXY_EXCLUDE_REGEX_KEY = "view.confirmRemoveProxyExcludeRegex"; @@ -142,7 +153,7 @@ public class OptionsParamView extends AbstractParam { private int largeResponseSize = LargeResponseUtil.DEFAULT_MIN_CONTENT_LENGTH; private boolean scaleImages = true; private boolean showDevWarning = true; - private String lookAndFeel = ""; + private LookAndFeelInfo lookAndFeelInfo = DEFAULT_LOOK_AND_FEEL; private boolean confirmRemoveProxyExcludeRegex; private boolean confirmRemoveScannerExcludeRegex; @@ -204,7 +215,9 @@ protected void parse() { scaleImages = getBoolean(SCALE_IMAGES, true); showDevWarning = getBoolean(SHOW_DEV_WARNING, true); - lookAndFeel = getString(LOOK_AND_FEEL, ""); + lookAndFeelInfo = + new LookAndFeelInfo( + getString(LOOK_AND_FEEL, ""), getString(LOOK_AND_FEEL_CLASS, "")); // Special cases - set via static methods LargeRequestUtil.setMinContentLength(largeRequestSize); @@ -535,13 +548,52 @@ public void setFontName(FontUtils.FontType fontType, String fontName) { getConfig().setProperty(getFontNameConfKey(fontType), fontName); } + /** + * Gets the the name of the selected look and feel. + * + * @return the name, might be {@code null} or empty if none selected (i.e. using default). + * @see #getLookAndFeelInfo() + * @since 2.8.0 + */ public String getLookAndFeel() { - return this.lookAndFeel; + return this.lookAndFeelInfo.getName(); } + /** + * Sets the name of the selected look and feel. + * + * @param lookAndFeel the name. + * @since 2.8.0 + * @deprecated (TODO add version) Use {@link #setLookAndFeelInfo(LookAndFeelInfo)} instead, + * which preserves the class of the look and feel. + */ + @Deprecated public void setLookAndFeel(String lookAndFeel) { - this.lookAndFeel = lookAndFeel; - getConfig().setProperty(LOOK_AND_FEEL, lookAndFeel); + setLookAndFeelInfo(new LookAndFeelInfo(lookAndFeel, "")); + } + + /** + * Gets the info of the selected look and feel. + * + * @return the info of the look and feel. + * @since TODO add version + * @see #getLookAndFeel() + */ + public LookAndFeelInfo getLookAndFeelInfo() { + return this.lookAndFeelInfo; + } + + /** + * Sets the info of the selected look and feel. + * + * @param lookAndFeelInfo the info of the look and feel. + * @throws NullPointerException if the given parameter is null. + * @since TODO add version + */ + public void setLookAndFeelInfo(LookAndFeelInfo lookAndFeelInfo) { + this.lookAndFeelInfo = Objects.requireNonNull(lookAndFeelInfo); + getConfig().setProperty(LOOK_AND_FEEL, lookAndFeelInfo.getName()); + getConfig().setProperty(LOOK_AND_FEEL_CLASS, lookAndFeelInfo.getClassName()); } public boolean isScaleImages() { diff --git a/zap/src/main/java/org/parosproxy/paros/extension/option/OptionsViewPanel.java b/zap/src/main/java/org/parosproxy/paros/extension/option/OptionsViewPanel.java index 1fca66db886..ddb9262aff9 100644 --- a/zap/src/main/java/org/parosproxy/paros/extension/option/OptionsViewPanel.java +++ b/zap/src/main/java/org/parosproxy/paros/extension/option/OptionsViewPanel.java @@ -34,6 +34,7 @@ // ZAP: 2018/06/11 Added options for Work Panels Font. // ZAP: 2019/06/01 Normalise line endings. // ZAP: 2019/06/05 Normalise format/style. +// ZAP: 2020/02/24 Use LookAndFeelInfo when setting the look and feel option. package org.parosproxy.paros.extension.option; import java.awt.BorderLayout; @@ -48,6 +49,7 @@ import java.awt.event.ItemEvent; import java.util.EnumMap; import java.util.Map; +import java.util.function.Predicate; import javax.swing.BorderFactory; import javax.swing.DefaultListCellRenderer; import javax.swing.JCheckBox; @@ -57,6 +59,7 @@ import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.UIManager; +import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -107,7 +110,7 @@ public class OptionsViewPanel extends AbstractParamPanel { private JComboBox displaySelect = null; private JComboBox responsePanelPositionComboBox; private JComboBox timeStampsFormatSelect = null; - private JComboBox lookAndFeel = null; + private JComboBox lookAndFeel = null; private ZapNumberSpinner largeRequestSize = null; private ZapNumberSpinner largeResponseSize = null; @@ -701,14 +704,14 @@ private JCheckBox getScaleImages() { return scaleImages; } - private JComboBox getLookAndFeelSelect() { + private JComboBox getLookAndFeelSelect() { if (lookAndFeel == null) { - lookAndFeel = new JComboBox(); + lookAndFeel = new JComboBox<>(); lookAndFeel.setMaximumRowCount(5); UIManager.LookAndFeelInfo[] looks = UIManager.getInstalledLookAndFeels(); - lookAndFeel.addItem(""); // Default look + lookAndFeel.addItem(new LookAndFeelInfoUi(OptionsParamView.DEFAULT_LOOK_AND_FEEL)); for (UIManager.LookAndFeelInfo look : looks) { - lookAndFeel.addItem(look.getName()); + lookAndFeel.addItem(new LookAndFeelInfoUi(look)); } } return lookAndFeel; @@ -721,7 +724,10 @@ public void initParam(Object obj) { getShowSplashScreen().setSelected(options.getViewParam().isShowSplashScreen()); getChkProcessImages().setSelected(options.getViewParam().getProcessImages() > 0); displaySelect.setSelectedIndex(options.getViewParam().getDisplayOption()); - selectResponsePanelPosition(options.getViewParam().getResponsePanelPosition()); + String panelPosition = options.getViewParam().getResponsePanelPosition(); + selectItem( + getResponsePanelPositionComboBox(), + item -> item.getPosition().name().equals(panelPosition)); brkPanelViewSelect.setSelectedIndex(options.getViewParam().getBrkPanelViewOption()); getChkShowMainToolbar().setSelected(options.getViewParam().isShowMainToolbar()); chkAdvancedView.setSelected(options.getViewParam().getAdvancedViewOption() > 0); @@ -741,20 +747,23 @@ public void initParam(Object obj) { } getScaleImages().setSelected(options.getViewParam().isScaleImages()); - getLookAndFeelSelect().setSelectedItem(options.getViewParam().getLookAndFeel()); + String nameLaf = options.getViewParam().getLookAndFeelInfo().getName(); + selectItem( + getLookAndFeelSelect(), + item -> item.getLookAndFeelInfo().getName().equals(nameLaf)); } - private void selectResponsePanelPosition(String positionName) { - for (int i = 0; i < getResponsePanelPositionComboBox().getItemCount(); i++) { - ResponsePanelPositionUI item = getResponsePanelPositionComboBox().getItemAt(i); - if (item.getPosition().name().equals(positionName)) { - getResponsePanelPositionComboBox().setSelectedIndex(i); + private static void selectItem(JComboBox comboBox, Predicate predicate) { + for (int i = 0; i < comboBox.getItemCount(); i++) { + T item = comboBox.getItemAt(i); + if (predicate.test(item)) { + comboBox.setSelectedIndex(i); break; } } - if (getResponsePanelPositionComboBox().getSelectedIndex() == -1) { - getResponsePanelPositionComboBox().setSelectedIndex(0); + if (comboBox.getSelectedIndex() == -1) { + comboBox.setSelectedIndex(0); } } @@ -788,7 +797,10 @@ public void saveParam(Object obj) throws Exception { .setFontName(fontType, (String) getFontName(fontType).getSelectedItem()); } options.getViewParam().setScaleImages(getScaleImages().isSelected()); - options.getViewParam().setLookAndFeel((String) getLookAndFeelSelect().getSelectedItem()); + options.getViewParam() + .setLookAndFeelInfo( + ((LookAndFeelInfoUi) getLookAndFeelSelect().getSelectedItem()) + .getLookAndFeelInfo()); } @Override @@ -838,4 +850,22 @@ public String toString() { return name; } } + + private static class LookAndFeelInfoUi { + + private final LookAndFeelInfo lookAndFeelInfo; + + LookAndFeelInfoUi(LookAndFeelInfo lookAndFeelInfo) { + this.lookAndFeelInfo = lookAndFeelInfo; + } + + LookAndFeelInfo getLookAndFeelInfo() { + return lookAndFeelInfo; + } + + @Override + public String toString() { + return lookAndFeelInfo.getName(); + } + } } diff --git a/zap/src/main/java/org/zaproxy/zap/GuiBootstrap.java b/zap/src/main/java/org/zaproxy/zap/GuiBootstrap.java index 97f529fe301..fc4413650f0 100644 --- a/zap/src/main/java/org/zaproxy/zap/GuiBootstrap.java +++ b/zap/src/main/java/org/zaproxy/zap/GuiBootstrap.java @@ -342,7 +342,8 @@ private void setupLookAndFeel() { OptionsParam options = Model.getSingleton().getOptionsParam(); - if (setLookAndFeel(getLookAndFeelClassname(options.getViewParam().getLookAndFeel()))) { + if (setLookAndFeel(getLookAndFeelClassname(options.getViewParam().getLookAndFeel())) + || setLookAndFeel(options.getViewParam().getLookAndFeelInfo().getClassName())) { return; } diff --git a/zap/src/main/java/org/zaproxy/zap/extension/uiutils/ExtensionUiUtils.java b/zap/src/main/java/org/zaproxy/zap/extension/uiutils/ExtensionUiUtils.java index 6155ce8f3ff..bc0cdd2697a 100644 --- a/zap/src/main/java/org/zaproxy/zap/extension/uiutils/ExtensionUiUtils.java +++ b/zap/src/main/java/org/zaproxy/zap/extension/uiutils/ExtensionUiUtils.java @@ -20,12 +20,16 @@ package org.zaproxy.zap.extension.uiutils; import java.awt.EventQueue; +import java.util.Arrays; +import javax.swing.UIManager; +import javax.swing.UIManager.LookAndFeelInfo; import org.apache.log4j.Logger; import org.parosproxy.paros.Constant; import org.parosproxy.paros.control.Control.Mode; import org.parosproxy.paros.extension.ExtensionAdaptor; import org.parosproxy.paros.extension.ExtensionHook; import org.parosproxy.paros.extension.SessionChangedListener; +import org.parosproxy.paros.extension.ViewDelegate; import org.parosproxy.paros.model.Session; import org.parosproxy.paros.view.MainFrame; @@ -52,6 +56,16 @@ public String getUIName() { return Constant.messages.getString("uiutils.name"); } + @Override + public void initView(ViewDelegate view) { + Arrays.asList( + new LookAndFeelInfo("Flat Light", "com.formdev.flatlaf.FlatLightLaf"), + new LookAndFeelInfo("Flat Dark", "com.formdev.flatlaf.FlatDarkLaf"), + new LookAndFeelInfo("Flat IntelliJ", "com.formdev.flatlaf.FlatIntelliJLaf"), + new LookAndFeelInfo("Flat Darcula", "com.formdev.flatlaf.FlatDarculaLaf")) + .forEach(UIManager::installLookAndFeel); + } + @Override public void hook(ExtensionHook extensionHook) { super.hook(extensionHook); diff --git a/zap/zap.gradle.kts b/zap/zap.gradle.kts index e3f992b03c8..3efe0f64ac5 100644 --- a/zap/zap.gradle.kts +++ b/zap/zap.gradle.kts @@ -66,6 +66,7 @@ dependencies { setTransitive(false) } implementation("org.javadelight:delight-nashorn-sandbox:0.1.26") + implementation("com.formdev:flatlaf:0.27") runtimeOnly("commons-jxpath:commons-jxpath:1.3") runtimeOnly("commons-logging:commons-logging:1.2")