Browse Source

REPORT-127437 fix: 插件图标适配代码调整

fbp/feature
lemon 3 months ago
parent
commit
9513c664c4
  1. 55
      designer-base/src/main/java/com/fine/theme/icon/IconManager.java
  2. 127
      designer-base/src/main/java/com/fine/theme/icon/plugin/PluginIconSet.java
  3. 22
      designer-base/src/main/java/com/fine/theme/icon/plugin/PluginJsonIconSet.java
  4. 57
      designer-base/src/main/java/com/fine/theme/icon/plugin/PluginListIconSet.java
  5. 5
      designer-base/src/main/java/com/fine/theme/light/ui/laf/FineDarkLaf.java
  6. 74
      designer-base/src/main/java/com/fine/theme/light/ui/laf/FineLaf.java
  7. 4
      designer-base/src/main/java/com/fine/theme/light/ui/laf/FineLightLaf.java
  8. 31
      designer-base/src/main/java/com/fr/design/fun/LazyIconProvider.java
  9. 21
      designer-base/src/main/java/com/fr/design/fun/impl/AbstractLazyIconProvider.java
  10. 3
      designer-base/src/main/java/com/fr/design/mainframe/toolbar/LookAndFeelAction.java

55
designer-base/src/main/java/com/fine/theme/icon/IconManager.java

@ -1,5 +1,8 @@
package com.fine.theme.icon;
import com.fine.theme.icon.plugin.PluginIconSet;
import com.fine.theme.light.ui.FineLightIconSet;
import com.formdev.flatlaf.FlatLaf;
import com.fr.base.extension.FileExtension;
import com.fr.general.IOUtils;
import com.fr.log.FineLoggerFactory;
@ -9,12 +12,13 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import java.awt.Dimension;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 图标管理器
@ -29,11 +33,41 @@ import java.util.concurrent.CopyOnWriteArrayList;
*/
@Immutable
public class IconManager {
public static final String ICON_DISABLE_SUFFIX = "_disable";
public static final Dimension DEFAULT_DIMENSION = new Dimension(16, 16);
private static final CopyOnWriteArrayList<IconSet> ICON_SETS = new CopyOnWriteArrayList<>();
private static final Map<String, WeakReference<Icon>> CACHE = new ConcurrentHashMap<>(64);
private static final ArrayList<IconSet> ICON_SETS = new ArrayList<>();
private static final HashMap<String, WeakReference<Icon>> CACHE = new HashMap<>(64);
/**
* 初始化 IconFineIconPluginIcon
*/
public static void initializeIcon() {
addIconSet();
}
/**
* 切换 Icon
*/
public static void updateIcon() {
ICON_SETS.clear();
clearCache();
addIconSet();
}
private static void addIconSet() {
boolean dark = false;
LookAndFeel laf = UIManager.getLookAndFeel();
if (laf instanceof FlatLaf) {
dark = ((FlatLaf) laf).isDark();
}
if (dark) {
// dark 主题还没适配
addSet(new FineLightIconSet());
} else {
addSet(new FineLightIconSet());
}
addSet(new PluginIconSet());
}
/**
@ -43,10 +77,17 @@ public class IconManager {
* @return 图标集
*/
public static IconSet getSet(String id) {
IconSet iconSet = null;
for (IconSet set : ICON_SETS) {
if (set.getId().equals(id)) {
return set;
iconSet = set;
}
if (set instanceof PluginIconSet && iconSet == null) {
iconSet = PluginIconSet.getIconSet(id);
}
}
if (iconSet != null) {
return iconSet;
}
throw new IconException("[IconManager] Can not find icon set by id: " + id);
}

127
designer-base/src/main/java/com/fine/theme/icon/plugin/PluginIconSet.java

@ -0,0 +1,127 @@
package com.fine.theme.icon.plugin;
import com.fine.theme.icon.AbstractIconSet;
import com.fine.theme.icon.IconSet;
import com.fine.theme.icon.IconType;
import com.fine.theme.icon.JsonIconSet;
import com.fine.theme.icon.UrlIconResource;
import com.formdev.flatlaf.FlatLaf;
import com.fr.design.fun.LazyIconProvider;
import com.fr.general.GeneralContext;
import com.fr.plugin.manage.PluginFilter;
import com.fr.plugin.observer.PluginEvent;
import com.fr.plugin.observer.PluginEventListener;
import com.fr.plugin.observer.PluginEventType;
import com.fr.stable.AssistUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Set;
import java.util.function.Consumer;
/**
* 管理插件 iconSet
* @author lemon
* @since
* Created on
*/
public class PluginIconSet extends AbstractIconSet {
private static final String NAME = "Plugin Icon Set";
private static final ArrayList<IconSet> PLUGIN_ICON_SETS = new ArrayList<>();
public PluginIconSet() {
name = NAME;
listenPluginIcons();
}
/**
* 适配插件图标 Icon
*/
public static void listenPluginIcons() {
//注册插件监听
PluginFilter filter = context -> context.contain(LazyIconProvider.MARK_STRING);
PluginEventListener insert = new PluginEventListener() {
@Override
public void on(PluginEvent event) {
handlePluginEvent(event, (provider) -> PLUGIN_ICON_SETS.add(generateJsonIconSet(provider)));
}
};
PluginEventListener remove = new PluginEventListener() {
@Override
public void on(PluginEvent event) {
handlePluginEvent(event, (provider) -> PLUGIN_ICON_SETS.removeIf(iconSet -> iconSet.getId().equals(provider.pluginId())));
}
};
GeneralContext.listenPlugin(PluginEventType.AfterRun, insert, filter);
GeneralContext.listenPlugin(PluginEventType.AfterInstall, insert, filter);
GeneralContext.listenPlugin(PluginEventType.AfterForbid, remove, filter);
GeneralContext.listenPlugin(PluginEventType.AfterUninstall, remove, filter);
}
private static void handlePluginEvent(PluginEvent event, Consumer<LazyIconProvider> consumer) {
Set<LazyIconProvider> set = event.getContext().getRuntime().get(LazyIconProvider.MARK_STRING);
for (LazyIconProvider provider : set) {
consumer.accept(provider);
}
}
private static JsonIconSet generateJsonIconSet(LazyIconProvider provider) {
LookAndFeel laf = UIManager.getLookAndFeel();
boolean dark = ((FlatLaf) laf).isDark();
String jsonPath = dark ? provider.darkJsonPath() : provider.lightJsonPath();
return new JsonIconSet(new UrlIconResource(jsonPath)) {
@Override
public @NotNull String getId() {
return provider.pluginId();
}
};
}
@Override
public @Nullable Icon findIcon(@NotNull String id, @NotNull Dimension dimension, IconType type) {
Icon icon;
for (IconSet iconSet : PLUGIN_ICON_SETS) {
icon = iconSet.findIcon(id, dimension, type);
if (icon != null) {
return icon;
}
}
return null;
}
/**
* 根据 id 匹配 icon set
* @param id 对于 plugin icon set, id plugin_id
* @return icon set
*/
public static IconSet getIconSet(@NotNull final String id ) {
for (IconSet iconSet : PLUGIN_ICON_SETS) {
if (iconSet.getId().equals(id)) {
return iconSet;
}
}
return null;
}
@Override
public boolean equals(Object obj) {
return obj instanceof PluginIconSet
&& AssistUtils.equals(this.name, ((PluginIconSet) obj).name);
}
@Override
public int hashCode() {
return AssistUtils.hashCode(name);
}
}

22
designer-base/src/main/java/com/fine/theme/icon/plugin/PluginJsonIconSet.java

@ -1,22 +0,0 @@
package com.fine.theme.icon.plugin;
import com.fine.theme.icon.JsonIconSet;
import com.fine.theme.icon.UrlIconResource;
/**
* 插件 json 图标集
* json 格式参考 fine_light.icon.json
* 为了保证 插件之间插件与设计器 icon id 的唯一性icons key 值规则参考 {@link PluginListIconSet}
*
* @author lemon
* @since
* Created on 2024/08/20
*/
public class PluginJsonIconSet extends JsonIconSet {
public PluginJsonIconSet(String id, UrlIconResource resource) {
super(resource);
this.name = id;
}
}

57
designer-base/src/main/java/com/fine/theme/icon/plugin/PluginListIconSet.java

@ -1,57 +0,0 @@
package com.fine.theme.icon.plugin;
import com.fine.theme.icon.AbstractIconSet;
import com.fine.theme.icon.IconManager;
import com.fine.theme.icon.img.ImageIconSource;
import com.fine.theme.icon.svg.SvgIconSource;
import java.util.List;
import java.util.Objects;
/**
* 插件 map 图标集
* 为了保证 插件之间插件与设计器 icon id 的唯一性以图标 path id 进行注册
*
* @author lemon
* @since
* Created on 2024/08/20
*/
public class PluginListIconSet extends AbstractIconSet {
public PluginListIconSet(String id, List<String> icons) {
this.name = id;
addIconWithList(icons);
}
/**
* 根据 list 注册图标
* @param icons icon path list
*/
public void addIconWithList(List<String> icons) {
for (String path : icons) {
if (IconManager.isSvgIcon(path)) {
addIcon(new SvgIconSource(path, path));
} else if (IconManager.isImageIcon(path)) {
addIcon(new ImageIconSource(path, path));
}
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PluginListIconSet that = (PluginListIconSet) o;
return Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hashCode(name);
}
}

5
designer-base/src/main/java/com/fine/theme/light/ui/laf/FineDarkLaf.java

@ -2,7 +2,6 @@ package com.fine.theme.light.ui.laf;
import com.fine.swing.ui.layout.Layouts;
import com.fine.theme.icon.IconManager;
import com.fine.theme.light.ui.FineLightIconSet;
import com.formdev.flatlaf.util.UIScale;
import com.fr.stable.StringUtils;
@ -25,15 +24,13 @@ public class FineDarkLaf extends FineLaf {
* @return 是否安装成功
*/
public static boolean setup() {
IconManager.addSet(new FineLightIconSet());
IconManager.initializeIcon();
Layouts.setScaleFactor(UIScale.getUserScaleFactor());
UIScale.addPropertyChangeListener(evt -> {
if (StringUtils.equals(evt.getPropertyName(), USER_SCALE_FACTOR)) {
Layouts.setScaleFactor((float) evt.getNewValue());
}
});
// dark_icon 目前还没适配,先使用 light_icon
listenPluginIcons(false);
return setup(new FineDarkLaf());
}

74
designer-base/src/main/java/com/fine/theme/light/ui/laf/FineLaf.java

@ -1,25 +1,9 @@
package com.fine.theme.light.ui.laf;
import com.fine.theme.icon.IconManager;
import com.fine.theme.icon.IconSet;
import com.fine.theme.icon.UrlIconResource;
import com.fine.theme.icon.plugin.PluginJsonIconSet;
import com.fine.theme.icon.plugin.PluginListIconSet;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.SystemInfo;
import com.fr.design.fun.LazyIconProvider;
import com.fr.general.GeneralContext;
import com.fr.nx.app.web.out.widget.utils.CollectionUtils;
import com.fr.plugin.manage.PluginFilter;
import com.fr.plugin.observer.PluginEvent;
import com.fr.plugin.observer.PluginEventListener;
import com.fr.plugin.observer.PluginEventType;
import javax.swing.PopupFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
/**
* Fine designer new look and feel
@ -34,36 +18,6 @@ public abstract class FineLaf extends FlatLaf {
private static final String NAME = "FineLaf";
private static void handlePluginEvent(PluginEvent event, boolean isDark,
BiConsumer<LazyIconProvider, List<IconSet>> operation) {
Set<LazyIconProvider> set = event.getContext().getRuntime().get(LazyIconProvider.MARK_STRING);
dealWithPluginIcon(set, isDark, operation);
}
private static void dealWithPluginIcon(Set<LazyIconProvider> set, boolean isDark,
BiConsumer<LazyIconProvider, List<IconSet>> operation) {
for (LazyIconProvider provider : set) {
if (provider.isDark() != isDark) {
continue;
}
List<IconSet> iconSets = generatePluginIconSet(provider);
operation.accept(provider, iconSets);
}
}
private static List<IconSet> generatePluginIconSet(LazyIconProvider provider) {
List<IconSet> iconSets = new ArrayList<>();
if (provider.jsonPath() != null) {
PluginJsonIconSet jsonIconSet = new PluginJsonIconSet(provider.id(), new UrlIconResource(provider.jsonPath()));
iconSets.add(jsonIconSet);
}
if (CollectionUtils.isNotEmpty(provider.icons())) {
PluginListIconSet listIconSet = new PluginListIconSet(provider.id(), provider.icons());
iconSets.add(listIconSet);
}
return iconSets;
}
@Override
public String getName() {
return NAME;
@ -99,32 +53,4 @@ public abstract class FineLaf extends FlatLaf {
}
}
/**
* 适配插件图标 Icon
* @param isDark 图标 Icon 主题light 或者 dark
*/
public static void listenPluginIcons(boolean isDark) {
//注册插件监听
PluginFilter filter = context -> context.contain(LazyIconProvider.MARK_STRING);
PluginEventListener insert = new PluginEventListener() {
@Override
public void on(PluginEvent event) {
handlePluginEvent(event, isDark, (provider, iconSets) -> IconManager.addSet(iconSets));
}
};
PluginEventListener remove = new PluginEventListener() {
@Override
public void on(PluginEvent event) {
handlePluginEvent(event, isDark, (provider, iconSets) -> IconManager.removeSet(provider.id()));
}
};
GeneralContext.listenPlugin(PluginEventType.AfterRun, insert, filter);
GeneralContext.listenPlugin(PluginEventType.AfterInstall, insert, filter);
GeneralContext.listenPlugin(PluginEventType.AfterForbid, remove, filter);
GeneralContext.listenPlugin(PluginEventType.AfterUninstall, remove, filter);
}
}

4
designer-base/src/main/java/com/fine/theme/light/ui/laf/FineLightLaf.java

@ -2,7 +2,6 @@ package com.fine.theme.light.ui.laf;
import com.fine.swing.ui.layout.Layouts;
import com.fine.theme.icon.IconManager;
import com.fine.theme.light.ui.FineLightIconSet;
import com.formdev.flatlaf.util.UIScale;
import com.fr.stable.StringUtils;
@ -25,14 +24,13 @@ public class FineLightLaf extends FineLaf {
* @return 是否安装成功
*/
public static boolean setup() {
IconManager.addSet(new FineLightIconSet());
IconManager.initializeIcon();
Layouts.setScaleFactor(UIScale.getUserScaleFactor());
UIScale.addPropertyChangeListener(evt -> {
if (StringUtils.equals(evt.getPropertyName(), USER_SCALE_FACTOR)) {
Layouts.setScaleFactor((float) evt.getNewValue());
}
});
listenPluginIcons(false);
return setup(new FineLightLaf());
}

31
designer-base/src/main/java/com/fr/design/fun/LazyIconProvider.java

@ -2,8 +2,6 @@ package com.fr.design.fun;
import com.fr.stable.fun.mark.Mutable;
import java.util.List;
/**
* 插件图标适配接口
*
@ -13,8 +11,6 @@ import java.util.List;
*/
public interface LazyIconProvider extends Mutable {
String MARK_STRING = "LazyIconProvider";
String DARK_SUFFIX = "_dark";
String LIGHT_SUFFIX = "_light";
int CURRENT_LEVEL = 1;
@ -25,32 +21,19 @@ public interface LazyIconProvider extends Mutable {
*/
String pluginId();
/**
* json 文件路径
* light 主题
*
* @return 图标注册 json 路径
*/
String jsonPath();
/**
* 构建需要注册的图标 key: id, value: icon path
* @return map
*/
List<String> icons();
String lightJsonPath();
/**
* 主题类别 light, dark
* @return 是否是 dark
* dark 主题
*
* @return 图标注册 json 路径
*/
boolean isDark();
String darkJsonPath();
/**
* 插件注册的 iconSet id 根据主题设置为 pluginId_light 或者 pluginId_dark
* 同种主题内部的 iconSet id 不做区分
* @return iconSet id
*/
default String id() {
String suffix = isDark() ? DARK_SUFFIX : LIGHT_SUFFIX;
return pluginId() + suffix;
}
}

21
designer-base/src/main/java/com/fr/design/fun/impl/AbstractLazyIconProvider.java

@ -15,7 +15,7 @@ import java.util.List;
* Created on
*/
@API(level = LazyIconProvider.CURRENT_LEVEL)
public class AbstractLazyIconProvider extends AbstractProvider implements LazyIconProvider {
public abstract class AbstractLazyIconProvider extends AbstractProvider implements LazyIconProvider {
/**
* 当前接口的API等级,用于判断是否需要升级插件
@ -37,28 +37,23 @@ public class AbstractLazyIconProvider extends AbstractProvider implements LazyIc
}
/**
* 通过 json 注册图标
* light 主题
*
* @return 图标注册 json 路径
*/
@Override
public String jsonPath() {
return null;
public String lightJsonPath() {
return "";
}
/**
* 直接注册图标元素是 icon path
* dark 主题
*
* @return list
* @return 图标注册 json 路径
*/
@Override
public List<String> icons() {
return null;
}
@Override
public boolean isDark() {
return false;
public String darkJsonPath() {
return "";
}
}

3
designer-base/src/main/java/com/fr/design/mainframe/toolbar/LookAndFeelAction.java

@ -5,6 +5,7 @@
package com.fr.design.mainframe.toolbar;
import com.fine.theme.icon.IconManager;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.extras.FlatAnimatedLafChange;
import com.fr.design.actions.UpdateAction;
@ -32,6 +33,8 @@ public class LookAndFeelAction extends UpdateAction {
FlatAnimatedLafChange.showSnapshot();
try {
UIManager.setLookAndFeel( lookAndFeel );
// 多主题场景下,需要判断是否需要切换图标
IconManager.updateIcon();
} catch (UnsupportedLookAndFeelException e) {
throw new RuntimeException(e);
}

Loading…
Cancel
Save