diff --git a/designer-base/src/main/java/com/fine/theme/icon/AbstractIconSet.java b/designer-base/src/main/java/com/fine/theme/icon/AbstractIconSet.java new file mode 100644 index 0000000000..f565077ffc --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/icon/AbstractIconSet.java @@ -0,0 +1,79 @@ +package com.fine.theme.icon; + +import com.fr.third.errorprone.annotations.Immutable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.Icon; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 抽象图标集 + * + * @author vito + * @since 11.0 + * Created on 2023/11/15 + */ +@Immutable +public abstract class AbstractIconSet implements IconSet { + + private final String id; + + private final Map> map = new ConcurrentHashMap<>(64); + private final Map cache = new ConcurrentHashMap<>(64); + private final Map disableCache = new ConcurrentHashMap<>(64); + + public AbstractIconSet(String id) { + this.id = id; + } + + @Override + public @NotNull String getId() { + return id; + } + + @Override + public @NotNull Collection getIds() { + return map.keySet(); + } + + @Override + public void addIcon(@NotNull IconSource icon) { + map.put(icon.getId(), icon); + } + + @Override + public void addIcon(@NotNull IconSource... icons) { + for (IconSource icon: icons){ + map.put(icon.getId(), icon); + } + } + + @Override + public @Nullable Icon findIcon(@NotNull String id) { + Icon icon = cache.get(id); + if (icon == null) { + IconSource iconSource = map.get(id); + if (iconSource != null) { + icon = iconSource.loadIcon(); + cache.put(id, icon); + } + } + return icon; + } + + @Override + public @Nullable Icon findDisableIcon(@NotNull String id) { + Icon icon = disableCache.get(id); + if (icon == null) { + IconSource iconSource = map.get(id); + if (iconSource != null) { + icon = iconSource.disabled(); + disableCache.put(id, icon); + } + } + return icon; + } +} diff --git a/designer-base/src/main/java/com/fine/theme/icon/AbstractIconSource.java b/designer-base/src/main/java/com/fine/theme/icon/AbstractIconSource.java new file mode 100644 index 0000000000..134bbb2a69 --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/icon/AbstractIconSource.java @@ -0,0 +1,106 @@ +package com.fine.theme.icon; + +import com.fr.third.errorprone.annotations.Immutable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.Icon; + +/** + * 抽象图标源 + * 能够根据图标源寻找灰化图标资源 + * + * @author vito + * @since 11.0 + * Created on 2023/11/14 + */ +@Immutable +public abstract class AbstractIconSource implements IconSource { + + public static final String ICON_DISABLE = "_disable"; + + protected String id; + + protected final IconResource resource; + + @Nullable + protected IconResource grayResource; + + protected boolean autoFindDisable = false; + + public AbstractIconSource(@NotNull final String id, @NotNull final IconResource resource) { + this.id = id; + this.resource = resource; + } + + public AbstractIconSource(@NotNull final String id, + @NotNull final IconResource resource, + final boolean autoFindDisable) { + this.id = id; + this.resource = resource; + this.autoFindDisable = autoFindDisable; + } + + + public AbstractIconSource(@NotNull final String id, + @NotNull final IconResource resource, + @Nullable final IconResource grayResource) { + this.id = id; + this.resource = resource; + this.grayResource = grayResource; + } + + @NotNull + @Override + public String getId() { + return id; + } + + @NotNull + @Override + public IconResource getResource() { + return resource; + } + + @NotNull + @Override + public I loadIcon() { + try { + return loadIcon(resource); + } catch (final Exception e) { + throw new IconException("Unable to load Icon: " + getId(), e); + } + } + + @NotNull + protected abstract I loadIcon(@NotNull IconResource resource); + + + /** + * 先找提供明确URL的灰化图, + * 再找指定自动寻找路径的灰化图, + * 最后再由具体图标提供默认灰化图 + * + * @return 灰化图 + */ + @Override + public @NotNull I disabled() { + if (grayResource != null) { + return loadIcon(grayResource); + } + if (autoFindDisable && resource instanceof UrlIconResource) { + String disablePath = findDisablePath(((UrlIconResource) resource).getPath()); + grayResource = new UrlIconResource(disablePath); + return loadIcon(grayResource); + } + return loadDisableIcon(); + } + + private static String findDisablePath(String path) { + int i = path.lastIndexOf('.'); + return path.substring(0, i) + ICON_DISABLE + path.substring(i); + } + + @NotNull + protected abstract I loadDisableIcon(); +} diff --git a/designer-base/src/main/java/com/fine/theme/icon/DisabledIcon.java b/designer-base/src/main/java/com/fine/theme/icon/DisabledIcon.java new file mode 100644 index 0000000000..242b49b4b7 --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/icon/DisabledIcon.java @@ -0,0 +1,23 @@ +package com.fine.theme.icon; + +import org.jetbrains.annotations.NotNull; + +import javax.swing.Icon; + +/** + * 创建一个灰化 Icon + * + * @author vito + * @since 11.0 + * Created on 2023/11/16 + */ +public interface DisabledIcon { + + /** + * 创建一份灰化图标 + * + * @return 灰化图标 + */ + @NotNull + Icon disabled(); +} diff --git a/designer-base/src/main/java/com/fine/theme/icon/IconException.java b/designer-base/src/main/java/com/fine/theme/icon/IconException.java new file mode 100644 index 0000000000..58087fb7b0 --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/icon/IconException.java @@ -0,0 +1,29 @@ +package com.fine.theme.icon; + +/** + * 图标异常 + * + * @author vito + * @since 11.0 + * Created on 2023/11/6 + */ +public class IconException extends RuntimeException { + public IconException() { + } + + public IconException(String message) { + super(message); + } + + public IconException(String message, Throwable cause) { + super(message, cause); + } + + public IconException(Throwable cause) { + super(cause); + } + + public IconException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/designer-base/src/main/java/com/fine/theme/icon/IconManager.java b/designer-base/src/main/java/com/fine/theme/icon/IconManager.java new file mode 100644 index 0000000000..3f9f6c7f76 --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/icon/IconManager.java @@ -0,0 +1,147 @@ +package com.fine.theme.icon; + +import com.fr.third.errorprone.annotations.Immutable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.Icon; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * 图标管理器 + * 1. 提供注册管理图标集,方便整体替换 + * 2. 提供图标缓存 + * 3. 查找图标 + * 4. 配合 {@link LazyIcon} 实现图标懒加载 + * + * @author vito + * @since 11.0 + * Created on 2023/9/12 + */ +@Immutable +public class IconManager { + + public static boolean initialized = false; + public static ArrayList iconSets = new ArrayList<>(2); + ; + public static HashMap> cache = new HashMap<>(60); + ; + public static HashMap> disableCache = new HashMap<>(60); + + + /** + * 获取图标集 + * + * @param id 图标集ID + * @return 图标集 + */ + public static IconSet getSet(String id) { + for (IconSet set : iconSets) { + if (set.getId().equals(id)) { + return set; + } + } + throw new IconException("[IconManager] Can not find icon set by id: " + id); + } + + /** + * 添加图标集 + * + * @param set 图标集 + */ + public static void addSet(@NotNull IconSet set) { + if (!iconSets.contains(set)) { + iconSets.add(set); + // 清理可能来自其他图集相同名称图标 + clearIconSetCache(set); + } else { + throw new IconException("[IconManager] IconSet by id:" + set.getId() + "is added!"); + } + } + + /** + * 根据图标ID获取图标 + * + * @param id 图标ID + * @param 图标类型 + * @return 图标 + */ + @NotNull + public static I getIcon(String id) { + Icon icon = findIcon(id); + if (icon == null) { + throw new IconException("[IconManager] Can not find icon by id: " + id); + } + return (I) icon; + } + + /** + * 获取灰化图标 + * + * @param id 图标ID + * @param 图标类型 + * @return 图标 + */ + @NotNull + public static I getDisableIcon(String id) { + Icon icon = findDisableIcon(id); + if (icon == null) { + throw new IconException("[IconManager] Can not find icon by id: " + id); + } + return (I) icon; + } + + @Nullable + private static I findDisableIcon(String id) { + final WeakReference reference = disableCache.get(id); + I icon = reference != null ? (I) reference.get() : null; + if (icon == null) { + for (IconSet set : iconSets) { + Icon f = set.findDisableIcon(id); + if (f != null) { + icon = (I) f; + disableCache.put(id, new WeakReference<>(icon)); + } + } + } + return icon; + } + + @Nullable + private static I findIcon(String id) { + final WeakReference reference = cache.get(id); + I icon = reference != null ? (I) reference.get() : null; + if (icon == null) { + for (IconSet set : iconSets) { + Icon f = set.findIcon(id); + if (f != null) { + icon = (I) f; + cache.put(id, new WeakReference<>(icon)); + } + } + } + return icon; + } + + /** + * 清理所有缓存 + */ + public static void clearCache() { + cache.clear(); + } + + /** + * 清理图标缓存 + */ + public static void clearIconCache(String id) { + cache.remove(id); + } + + private static void clearIconSetCache(@NotNull IconSet set) { + for (String id : set.getIds()) { + cache.remove(id); + } + } +} diff --git a/designer-base/src/main/java/com/fine/theme/icon/IconResource.java b/designer-base/src/main/java/com/fine/theme/icon/IconResource.java new file mode 100644 index 0000000000..f654a6cf8c --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/icon/IconResource.java @@ -0,0 +1,23 @@ +package com.fine.theme.icon; + +import org.jetbrains.annotations.Nullable; + +import java.io.InputStream; + +/** + * 资源接口 + * + * @author vito + * @since 11.0 + * Created on 2023/11/6 + */ +public interface IconResource { + + /** + * 获取输入资源流 + * + * @return 资源流 + */ + @Nullable + InputStream getInputStream(); +} diff --git a/designer-base/src/main/java/com/fine/theme/icon/IconSet.java b/designer-base/src/main/java/com/fine/theme/icon/IconSet.java new file mode 100644 index 0000000000..f5f92d6bab --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/icon/IconSet.java @@ -0,0 +1,63 @@ +package com.fine.theme.icon; + +import com.fr.third.errorprone.annotations.Immutable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.Icon; +import java.util.Collection; + +/** + * 图标集 + * + * @author vito + * @since 11.0 + * Created on 2023/11/6 + */ +@Immutable +public interface IconSet extends Identifiable { + @NotNull + @Override + String getId(); + + /** + * 返回集合中所有 Icons 的 id。 + * + * @return 集合中所有 Icons 的 id。 + */ + @NotNull + Collection getIds(); + + /** + * 将指定 IconSource 引用的新 Icon 添加到集合中。 + * + * @param icon icon 源 + */ + void addIcon(@NotNull IconSource icon); + + /** + * 将指定 IconSource 引用的新 Icon 添加到集合中。 + * + * @param icon icon 源 + */ + void addIcon(@NotNull IconSource... icon); + + /** + * 返回指定 id 的 Icon。 + * + * @param id id + * @return Icon + */ + @Nullable + Icon findIcon(@NotNull String id); + + + /** + * 返回指定 id 的 Icon。 + * + * @param id id + * @return Icon + */ + @Nullable + Icon findDisableIcon(@NotNull String id); +} diff --git a/designer-base/src/main/java/com/fine/theme/icon/IconSource.java b/designer-base/src/main/java/com/fine/theme/icon/IconSource.java new file mode 100644 index 0000000000..ed2d1c6b76 --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/icon/IconSource.java @@ -0,0 +1,36 @@ +package com.fine.theme.icon; + +import com.fr.third.errorprone.annotations.Immutable; +import org.jetbrains.annotations.NotNull; + +import javax.swing.Icon; +import java.io.Serializable; + +/** + * 图标源,在进行图标管理的时候代替真实图标对象 + * 使用时加载,节省内存 + * + * @author vito + * @since 11.0 + * Created on 2023/11/6 + */ +@Immutable +public interface IconSource extends Identifiable, DisabledIcon, Cloneable, Serializable { + + /** + * 获取图标资源 + * + * @return 图标资源 + */ + @NotNull + IconResource getResource(); + + + /** + * 加载图标 + * + * @return 图标 + */ + @NotNull + I loadIcon(); +} diff --git a/designer-base/src/main/java/com/fine/theme/icon/Identifiable.java b/designer-base/src/main/java/com/fine/theme/icon/Identifiable.java new file mode 100644 index 0000000000..b1d32e4ae1 --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/icon/Identifiable.java @@ -0,0 +1,12 @@ +package com.fine.theme.icon; + +/** + * id 接口 + * + * @author vito + * @since 11.0 + * Created on 2023/11/6 + */ +public interface Identifiable { + String getId(); +} diff --git a/designer-base/src/main/java/com/fine/theme/icon/LazyIcon.java b/designer-base/src/main/java/com/fine/theme/icon/LazyIcon.java new file mode 100644 index 0000000000..aa77042fd4 --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/icon/LazyIcon.java @@ -0,0 +1,71 @@ +package com.fine.theme.icon; + +import com.fr.third.errorprone.annotations.Immutable; +import org.jetbrains.annotations.NotNull; + +import javax.swing.Icon; +import java.awt.Component; +import java.awt.Graphics; + +/** + * 懒加载图标 + * + * @author vito + * @since 11.0 + * Created on 2023/11/6 + */ +@Immutable +public class LazyIcon implements Identifiable, DisabledIcon, Icon { + @NotNull + private final String id; + + public LazyIcon(@NotNull final String id) { + this.id = id; + } + + + @NotNull + @Override + public String getId() { + return id; + } + + @Override + public void paintIcon(@NotNull final Component c, @NotNull final Graphics g, final int x, final int y) { + getIcon().paintIcon(c, g, x, y); + } + + @Override + public int getIconWidth() { + return getIcon().getIconWidth(); + } + + @Override + public int getIconHeight() { + return getIcon().getIconHeight(); + } + + + @NotNull + public I getIcon() { + return IconManager.getIcon(getId()); + } + + /** + * 创建一份灰化图标 + * + * @return 灰化图标 + */ + @NotNull + @Override + public Icon disabled() { + return IconManager.getDisableIcon(getId()); + } + + @NotNull + @Override + public String toString() { + return getClass().getCanonicalName() + "[id=" + getId() + "]"; + + } +} diff --git a/designer-base/src/main/java/com/fine/theme/icon/UrlIconResource.java b/designer-base/src/main/java/com/fine/theme/icon/UrlIconResource.java new file mode 100644 index 0000000000..58e53646e2 --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/icon/UrlIconResource.java @@ -0,0 +1,39 @@ +package com.fine.theme.icon; + +import com.fr.general.IOUtils; +import com.fr.io.utils.ResourceIOUtils; +import com.fr.third.errorprone.annotations.Immutable; + +import java.io.InputStream; + +/** + * url图标资源 + * + * @author vito + * @since 11.0 + * Created on 2023/11/15 + */ +@Immutable +public class UrlIconResource implements IconResource { + + private final String path; + + public UrlIconResource(String path) { + this.path = path; + } + + public String getPath() { + return path; + } + + @Override + public InputStream getInputStream() { + return getInputStream(path); + } + + + private InputStream getInputStream(String path) { + InputStream inputStream = IOUtils.getInputStream(path); + return inputStream != null ? inputStream : ResourceIOUtils.read(path); + } +} diff --git a/designer-base/src/main/java/com/fine/theme/icon/svg/SvgIcon.java b/designer-base/src/main/java/com/fine/theme/icon/svg/SvgIcon.java new file mode 100644 index 0000000000..1d81ed1504 --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/icon/svg/SvgIcon.java @@ -0,0 +1,159 @@ +package com.fine.theme.icon.svg; + +import com.fine.theme.icon.DisabledIcon; +import com.fine.theme.icon.IconResource; +import com.fr.clone.cloning.Immutable; +import org.apache.batik.transcoder.TranscoderException; +import org.apache.batik.transcoder.TranscoderInput; +import org.jetbrains.annotations.NotNull; + +import javax.swing.Icon; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +import static com.fine.theme.utils.FineUIScale.scale; +import static com.fine.theme.utils.FineUIScale.unscale; +import static com.fine.theme.utils.FineUIUtils.RETINA_SCALE_FACTOR; +import static com.fine.theme.utils.FineUIUtils.getRetina; + +/** + * svg图标 + * 1.绘制长度会跟随DPI比率变化 + * 2.如果设备支持 Retina 则支持Retina绘制HIDPI图 + * 1跟2的缩放原因不同,因此不能混淆,宽高测量等依旧 + * 使用DPI缩放进行,只有绘制内容时使用Retina绘制(如果有) + * Retina绘制不影响最终尺寸,注意区分 + * + * @author vito + * @since 11.0 + * Created on 2023/11/15 + */ +@Immutable +public class SvgIcon implements DisabledIcon, Icon { + + protected transient BufferedImage cache; + + private final Dimension size; + private final IconResource resource; + private final boolean disable; + + public SvgIcon(IconResource resource, Dimension size) { + this(resource, size, false); + } + + public SvgIcon(IconResource resource, Dimension size, boolean disable) { + this.resource = resource; + // 根据dpi进行缩放 + this.size = scale(size); + this.disable = disable; + } + + public SvgIcon(IconResource resource, int side) { + this(resource, new Dimension(side, side), false); + } + + /** + * 如果支持绘制Retina绘制,则进行Retina绘制, + * 绘制结束不影响任何外部尺寸 + */ + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + if (isCacheValid()) { + if (cache != null) { + cache.flush(); + } + Dimension scale = scaleRetina(size); + if (disable && c != null) { + cache = toGrayImage(scale, c.getBackground()); + } else { + cache = toImage(scale); + } + } + if (getRetina()) { + // 高清绘制的原理:scale(1/2,1/2)的原理是坐标减半,底层是矩阵进行坐标变换,意思是坐标减半进行绘制, + // 这样就可以将两倍图绘制到一倍的大小,如果这时候设备支持Retina绘制(四个像素模拟一个像素), + // 正好就可以将4个像素利用起来,每个像素点都有不同的颜色,而不像之前只能是四个共用一个颜色。因此图像 + // 可以更加细腻当然,绘图之后,需要将这个坐标变换给恢复。 + ((Graphics2D) g).scale(1.0 / RETINA_SCALE_FACTOR, 1.0 / RETINA_SCALE_FACTOR); + g.drawImage(cache, x * RETINA_SCALE_FACTOR, y * RETINA_SCALE_FACTOR, null); + ((Graphics2D) g).scale(RETINA_SCALE_FACTOR, RETINA_SCALE_FACTOR); + } else { + g.drawImage(cache, x, y, null); + } + } + + private boolean isCacheValid() { + return cache == null + || cache.getWidth() != scaleRetina(size.width) + || cache.getHeight() != scaleRetina(size.height); + } + + private static int scaleRetina(int size) { + return getRetina() + ? size * RETINA_SCALE_FACTOR + : size; + } + + private static Dimension scaleRetina(Dimension dimension) { + return getRetina() + ? new Dimension(dimension.width * RETINA_SCALE_FACTOR, dimension.height * RETINA_SCALE_FACTOR) + : dimension; + } + + @Override + public int getIconWidth() { + // 根据dpi解除缩放 + return unscale(size.width); + } + + @Override + public int getIconHeight() { + // 根据dpi解除缩放 + return unscale(size.height); + } + + /** + * 根据指定尺寸绘制图片,这里尺寸为结算后的尺寸, + * 因此不必进行缩放 + * + * @param size 图像尺寸 + * @return 图像 + */ + private BufferedImage toImage(Dimension size) { + SvgTranscoder transcoder = new SvgTranscoder(size); + TranscoderInput transcoderInput = new TranscoderInput(resource.getInputStream()); + try { + transcoder.transcode(transcoderInput, null); + return transcoder.getImage(); + } catch (TranscoderException e) { + throw new RuntimeException(e); + } + } + + /** + * 简单的灰化处理,不能有透明度,因此需要传入背景, + * 暂时用作灰度,后面再处理 + */ + private BufferedImage toGrayImage(Dimension size, Color background) { + SvgTranscoder transcoder = new SvgTranscoder(size, background, true); + TranscoderInput transcoderInput = new TranscoderInput(resource.getInputStream()); + try { + transcoder.transcode(transcoderInput, null); + return transcoder.getImage(); + } catch (TranscoderException e) { + throw new RuntimeException(e); + } + } + + /** + * 默认提供一个简单的灰化处理 + */ + @Override + public @NotNull SvgIcon disabled() { + return new SvgIcon(resource, size, true); + } +} diff --git a/designer-base/src/main/java/com/fine/theme/icon/svg/SvgIconSource.java b/designer-base/src/main/java/com/fine/theme/icon/svg/SvgIconSource.java new file mode 100644 index 0000000000..c7870a1037 --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/icon/svg/SvgIconSource.java @@ -0,0 +1,74 @@ +package com.fine.theme.icon.svg; + +import com.fine.theme.icon.AbstractIconSource; +import com.fine.theme.icon.IconResource; +import com.fine.theme.icon.UrlIconResource; +import com.fr.clone.cloning.Immutable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.Dimension; + +/** + * svg图标源 + * + * @author vito + * @since 11.0 + * Created on 2023/11/14 + */ +@Immutable +public class SvgIconSource extends AbstractIconSource { + + private static final int ICON_SIDE_LENGTH = 16; + + private final Dimension size; + + public SvgIconSource(@NotNull String id, @NotNull String resource) { + this(id, new UrlIconResource(resource), null, new Dimension(ICON_SIDE_LENGTH, ICON_SIDE_LENGTH)); + } + + public SvgIconSource(@NotNull String id, @NotNull String resource, boolean autoFindDisable) { + this(id, new UrlIconResource(resource), autoFindDisable, new Dimension(ICON_SIDE_LENGTH, ICON_SIDE_LENGTH)); + } + + public SvgIconSource(@NotNull String id, @NotNull String resource, int side) { + this(id, new UrlIconResource(resource), null, side); + } + + public SvgIconSource(@NotNull String id, @NotNull String resource, boolean autoFindDisable, int side) { + this(id, new UrlIconResource(resource), autoFindDisable, new Dimension(side, side)); + } + + public SvgIconSource(@NotNull String id, @NotNull String resource, @Nullable String grayResource, int side) { + this(id, new UrlIconResource(resource), new UrlIconResource(grayResource), side); + } + + public SvgIconSource(@NotNull String id, @NotNull IconResource resource, + @Nullable IconResource grayResource, int side) { + this(id, resource, grayResource, new Dimension(side, side)); + } + + public SvgIconSource(@NotNull String id, @NotNull IconResource resource, + boolean autoFindDisable, Dimension size) { + super(id, resource, autoFindDisable); + this.size = size; + } + + public SvgIconSource(@NotNull String id, @NotNull IconResource resource, + @Nullable IconResource grayResource, Dimension size) { + super(id, resource, grayResource); + this.size = size; + } + + + @NotNull + @Override + protected SvgIcon loadIcon(@NotNull IconResource resource) { + return new SvgIcon(resource, size); + } + + @Override + public @NotNull SvgIcon loadDisableIcon() { + return new SvgIcon(resource, size).disabled(); + } +} diff --git a/designer-base/src/main/java/com/fine/theme/icon/svg/SvgTranscoder.java b/designer-base/src/main/java/com/fine/theme/icon/svg/SvgTranscoder.java new file mode 100644 index 0000000000..abb55e1c72 --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/icon/svg/SvgTranscoder.java @@ -0,0 +1,65 @@ +package com.fine.theme.icon.svg; + +import org.apache.batik.transcoder.SVGAbstractTranscoder; +import org.apache.batik.transcoder.TranscoderException; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.image.ImageTranscoder; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.image.BufferedImage; + +/** + * Svg图标转码器 + * + * @author vito + * @since 11.0 + * Created on 2023/11/15 + */ +public class SvgTranscoder extends ImageTranscoder { + + private BufferedImage bufferedImage; + private boolean gray = false; + + public SvgTranscoder(Dimension size) { + addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH, (float) size.getWidth()); + addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT, (float) size.getHeight()); + } + + public SvgTranscoder(Dimension size, Color background, boolean gray) { + this.gray = gray; + addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH, (float) size.getWidth()); + addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT, (float) size.getHeight()); + addTranscodingHint(ImageTranscoder.KEY_BACKGROUND_COLOR, background); + } + + + public SvgTranscoder(float width, float height) { + addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH, width); + addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT, height); + } + + @Override + public BufferedImage createImage(int width, int height) { + return gray ? + createGrayImage(width, height) : + new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + } + + /** + * 灰化底图 + */ + private BufferedImage createGrayImage(int width, int height) { + return new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); + } + + + @Override + public void writeImage(BufferedImage bufferedImage, TranscoderOutput transcoderOutput) throws TranscoderException { + this.bufferedImage = bufferedImage; + } + + public BufferedImage getImage() { + return bufferedImage; + } +} diff --git a/designer-base/src/main/java/com/fine/theme/light/icon/IconManager.java b/designer-base/src/main/java/com/fine/theme/light/icon/IconManager.java deleted file mode 100644 index bc4bbeade8..0000000000 --- a/designer-base/src/main/java/com/fine/theme/light/icon/IconManager.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.fine.theme.light.icon; - -/** - * 图标管理器 - * - * @author vito - * @since 11.0 - * Created on 2023/9/12 - */ -public class IconManager { -} diff --git a/designer-base/src/main/java/com/fine/theme/light/ui/FineLightIconSet.java b/designer-base/src/main/java/com/fine/theme/light/ui/FineLightIconSet.java new file mode 100644 index 0000000000..d02166ad3e --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/light/ui/FineLightIconSet.java @@ -0,0 +1,34 @@ +package com.fine.theme.light.ui; + +import com.fine.theme.icon.AbstractIconSet; +import com.fine.theme.icon.svg.SvgIconSource; + +/** + * Fine 亮主题图标集 + * + * @author vito + * @since 11.0 + * Created on 2023/11/17 + */ +public class FineLightIconSet extends AbstractIconSet { + + public FineLightIconSet(String id) { + super(id); + load(); + } + + private void load() { + addIcon( + new SvgIconSource("cut", "com/fine/theme/icon/cut.svg", true), + new SvgIconSource("save", "com/fine/theme/icon/save.svg", true), + new SvgIconSource("copy", "com/fine/theme/icon/copy.svg", true), + new SvgIconSource("formatBrush", "com/fine/theme/icon/formatBrush.svg", true), + new SvgIconSource("paste", "com/fine/theme/icon/paste.svg", true), + new SvgIconSource("undo", "com/fine/theme/icon/undo.svg", true), + new SvgIconSource("redo", "com/fine/theme/icon/redo.svg", true), + new SvgIconSource("version_save", "com/fine/theme/icon/version_save.svg", true), + new SvgIconSource("font_miss_check", "com/fine/theme/icon/font_miss_check.svg", true), + new SvgIconSource("template_theme", "com/fine/theme/icon/template_theme.svg", true) + ); + } +} diff --git a/designer-base/src/main/java/com/fine/theme/light/ui/laf/FineLightLaf.java b/designer-base/src/main/java/com/fine/theme/light/ui/laf/FineLightLaf.java index 7b3bbcce1b..58442eca58 100644 --- a/designer-base/src/main/java/com/fine/theme/light/ui/laf/FineLightLaf.java +++ b/designer-base/src/main/java/com/fine/theme/light/ui/laf/FineLightLaf.java @@ -1,5 +1,7 @@ package com.fine.theme.light.ui.laf; +import com.fine.theme.light.ui.FineLightIconSet; +import com.fine.theme.icon.IconManager; import com.formdev.flatlaf.FlatLightLaf; /** @@ -11,7 +13,8 @@ import com.formdev.flatlaf.FlatLightLaf; */ public class FineLightLaf extends FlatLightLaf { public static boolean setup() { - return setup( new FineLightLaf() ); + IconManager.addSet(new FineLightIconSet("fine-light")); + return setup(new FineLightLaf()); } @Override @@ -19,6 +22,4 @@ public class FineLightLaf extends FlatLightLaf { return "FineLightLaf"; } - - } diff --git a/designer-base/src/main/java/com/fine/theme/light/utils/FineUIUtils.java b/designer-base/src/main/java/com/fine/theme/light/utils/FineUIUtils.java deleted file mode 100644 index 693600770e..0000000000 --- a/designer-base/src/main/java/com/fine/theme/light/utils/FineUIUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.fine.theme.light.utils; - -import javax.swing.UIManager; -import java.awt.Color; - -/** - * UI绘制的一些常用方法 - * - * @author vito - * @since 11.0 - * Created on 2023/11/3 - */ -public class FineUIUtils { - - - /** - * 通过key获取UI的颜色,如果没有则使用后备key获取 - * - * @param key 颜色key - * @param defaultKey 颜色后备key - * @return 颜色 - */ - - public static Color getUIColor(String key, String defaultKey) { - Color color = UIManager.getColor(key); - return (color != null) ? color : UIManager.getColor(defaultKey); - } - - /** - * 获取key指定的int值,如果没有则使用后备key获取 - * - * @param key int所在的key - * @param defaultKey 后备key - * @return 长度 - */ - public static int getUIInt(String key, String defaultKey) { - Object value = UIManager.get(key); - return (value instanceof Integer) ? (Integer) value : UIManager.getInt(defaultKey); - } -} diff --git a/designer-base/src/main/java/com/fine/theme/utils/FineUIScale.java b/designer-base/src/main/java/com/fine/theme/utils/FineUIScale.java new file mode 100644 index 0000000000..6297166c78 --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/utils/FineUIScale.java @@ -0,0 +1,86 @@ +package com.fine.theme.utils; + +import com.formdev.flatlaf.util.UIScale; + +import javax.swing.plaf.DimensionUIResource; +import javax.swing.plaf.InsetsUIResource; +import javax.swing.plaf.UIResource; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Insets; + +/** + * UI缩放工具 + * + * @author vito + * @since 11.0 + * Created on 2023/11/15 + */ +public class FineUIScale { + /** + * Multiplies the given value by the user scale factor. + */ + public static float scale(float value) { + return UIScale.scale(value); + } + + /** + * Multiplies the given value by the user scale factor and rounds the result. + */ + public static int scale(int value) { + return UIScale.scale(value); + } + + /** + * Similar as {@link #scale(int)} but always "rounds down". + *

+ * For use in special cases. {@link #scale(int)} is the preferred method. + */ + public static int scale2(int value) { + return UIScale.scale2(value); + } + + /** + * Divides the given value by the user scale factor. + */ + public static float unscale(float value) { + return UIScale.unscale(value); + } + + /** + * Divides the given value by the user scale factor and rounds the result. + */ + public static int unscale(int value) { + return UIScale.unscale(value); + } + + /** + * If user scale factor is not 1, scale the given graphics context by invoking + * {@link Graphics2D#scale(double, double)} with user scale factor. + */ + public static void scaleGraphics(Graphics2D g) { + UIScale.scaleGraphics(g); + } + + /** + * Scales the given dimension with the user scale factor. + *

+ * If user scale factor is 1, then the given dimension is simply returned. + * Otherwise, a new instance of {@link Dimension} or {@link DimensionUIResource} + * is returned, depending on whether the passed dimension implements {@link UIResource}. + */ + public static Dimension scale(Dimension dimension) { + return UIScale.scale(dimension); + } + + /** + * Scales the given insets with the user scale factor. + *

+ * If user scale factor is 1, then the given insets is simply returned. + * Otherwise, a new instance of {@link Insets} or {@link InsetsUIResource} + * is returned, depending on whether the passed dimension implements {@link UIResource}. + */ + public static Insets scale(Insets insets) { + return UIScale.scale(insets); + } +} diff --git a/designer-base/src/main/java/com/fine/theme/utils/FineUIUtils.java b/designer-base/src/main/java/com/fine/theme/utils/FineUIUtils.java new file mode 100644 index 0000000000..b4b6c9a301 --- /dev/null +++ b/designer-base/src/main/java/com/fine/theme/utils/FineUIUtils.java @@ -0,0 +1,90 @@ +package com.fine.theme.utils; + +import com.fr.stable.os.OperatingSystem; +import com.fr.value.AtomicClearableLazyValue; + +import javax.swing.UIManager; +import java.awt.Color; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.lang.reflect.Field; + +/** + * UI绘制的一些常用方法 + * + * @author vito + * @since 11.0 + * Created on 2023/11/3 + */ +public class FineUIUtils { + + public static final int RETINA_SCALE_FACTOR = 2; + + /** + * 判断是否支持retina,制作一些特殊效果,如HIDPI图片绘制。 + * retina 是一种特殊的效果,使用4个像素点模拟一个像素点, + * 因此在其他操作系统上,即使是高分屏也不具备retina的效果。 + * 甚至还有劣化的效果以及更差的性能。 + * + * @since 2023.11.16 + */ + private static final AtomicClearableLazyValue RETINA = AtomicClearableLazyValue.create(() -> { + // 经过测试win11,ubuntu等,没有retina效果 + if (!OperatingSystem.isMacos()) { + return false; + } + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice device = env.getDefaultScreenDevice(); + + try { + Field field = device.getClass().getDeclaredField("scale"); + field.setAccessible(true); + Object scale = field.get(device); + if (scale instanceof Integer && (Integer) scale == RETINA_SCALE_FACTOR) { + return true; + } + } catch (Exception ignored) { + } + return false; + }); + + /** + * 是否支持 retina + * + * @return 是否支持 retina + */ + public static boolean getRetina() { + return RETINA.getValue(); + } + + /** + * 放弃 retina 判断结果,用于清理或者切换环境 + */ + public static void clearRetina() { + RETINA.drop(); + } + + /** + * 通过key获取UI的颜色,如果没有则使用后备key获取 + * + * @param key 颜色key + * @param defaultKey 颜色后备key + * @return 颜色 + */ + public static Color getUIColor(String key, String defaultKey) { + Color color = UIManager.getColor(key); + return (color != null) ? color : UIManager.getColor(defaultKey); + } + + /** + * 获取key指定的int值,如果没有则使用后备key获取 + * + * @param key int所在的key + * @param defaultKey 后备key + * @return 长度 + */ + public static int getUIInt(String key, String defaultKey) { + Object value = UIManager.get(key); + return (value instanceof Integer) ? (Integer) value : UIManager.getInt(defaultKey); + } +} diff --git a/designer-base/src/main/java/com/fr/design/actions/UpdateAction.java b/designer-base/src/main/java/com/fr/design/actions/UpdateAction.java index d33c04eb15..b9734ac9b8 100644 --- a/designer-base/src/main/java/com/fr/design/actions/UpdateAction.java +++ b/designer-base/src/main/java/com/fr/design/actions/UpdateAction.java @@ -3,6 +3,7 @@ */ package com.fr.design.actions; +import com.fine.theme.icon.LazyIcon; import com.fr.base.NameStyle; import com.fr.base.ScreenResolution; import com.fr.base.Style; @@ -171,6 +172,11 @@ public abstract class UpdateAction extends ShortCut implements Action { * @param smallIcon The small icon for the action. */ public void setSmallIcon(Icon smallIcon) { + if(smallIcon instanceof LazyIcon){ + this.putValue(Action.SMALL_ICON, smallIcon); + this.putValue(UpdateAction.DISABLED_ICON,((LazyIcon) smallIcon).disabled()); + return; + } this.putValue(Action.SMALL_ICON, smallIcon); } @@ -677,7 +683,7 @@ public abstract class UpdateAction extends ShortCut implements Action { protected void setDisabledIcon4Button(AbstractButton button) { Icon disabledIcon = (Icon) this.getValue(UpdateAction.DISABLED_ICON); - if (disabledIcon != null && disabledIcon instanceof SVGIcon) { + if (disabledIcon != null) { button.setDisabledIcon(disabledIcon); } } diff --git a/designer-base/src/main/java/com/fr/design/actions/edit/CopyAction.java b/designer-base/src/main/java/com/fr/design/actions/edit/CopyAction.java index 0505a8e140..9f1eda60a9 100644 --- a/designer-base/src/main/java/com/fr/design/actions/edit/CopyAction.java +++ b/designer-base/src/main/java/com/fr/design/actions/edit/CopyAction.java @@ -3,6 +3,7 @@ */ package com.fr.design.actions.edit; +import com.fine.theme.icon.LazyIcon; import com.fr.design.actions.TemplateComponentAction; import com.fr.design.base.mode.DesignModeContext; import com.fr.design.designer.TargetComponent; @@ -21,7 +22,7 @@ public class CopyAction extends TemplateComponentAction { this.setName(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_M_Edit_Copy")); this.setMnemonic('C'); - this.setSmallIcon("/com/fr/design/standard/copy/copy"); + this.setSmallIcon(new LazyIcon("copy")); this.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, DEFAULT_MODIFIER)); this.setEnabled(!DesignModeContext.isBanCopyAndCut()); } diff --git a/designer-base/src/main/java/com/fr/design/actions/edit/CutAction.java b/designer-base/src/main/java/com/fr/design/actions/edit/CutAction.java index c15e962272..001c40d770 100644 --- a/designer-base/src/main/java/com/fr/design/actions/edit/CutAction.java +++ b/designer-base/src/main/java/com/fr/design/actions/edit/CutAction.java @@ -3,10 +3,10 @@ */ package com.fr.design.actions.edit; +import com.fine.theme.icon.LazyIcon; import com.fr.design.actions.TemplateComponentAction; import com.fr.design.base.mode.DesignModeContext; import com.fr.design.designer.TargetComponent; -import com.fr.general.IOUtils; import javax.swing.KeyStroke; import java.awt.event.KeyEvent; @@ -25,7 +25,7 @@ public class CutAction extends TemplateComponentAction { this.setName(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_M_Edit_Cut")); this.setMnemonic('T'); - this.setSmallIcon("/com/fr/design/standard/cut/cut"); + this.setSmallIcon(new LazyIcon("cut")); this.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, DEFAULT_MODIFIER)); this.setEnabled(!DesignModeContext.isBanCopyAndCut()); } diff --git a/designer-base/src/main/java/com/fr/design/actions/edit/PasteAction.java b/designer-base/src/main/java/com/fr/design/actions/edit/PasteAction.java index c14fcabf57..7319400faf 100644 --- a/designer-base/src/main/java/com/fr/design/actions/edit/PasteAction.java +++ b/designer-base/src/main/java/com/fr/design/actions/edit/PasteAction.java @@ -3,6 +3,7 @@ */ package com.fr.design.actions.edit; +import com.fine.theme.icon.LazyIcon; import com.fr.design.actions.TemplateComponentAction; import com.fr.design.base.mode.DesignModeContext; import com.fr.design.designer.TargetComponent; @@ -24,7 +25,7 @@ public class PasteAction extends TemplateComponentAction { this.setName(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_M_Edit_Paste")); this.setMnemonic('P'); - this.setSmallIcon("/com/fr/design/standard/paste/paste"); + this.setSmallIcon(new LazyIcon("paste")); this.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, DEFAULT_MODIFIER)); } diff --git a/designer-base/src/main/java/com/fr/design/gui/ibutton/UIButton.java b/designer-base/src/main/java/com/fr/design/gui/ibutton/UIButton.java index f42266dc1f..f6c37d815a 100644 --- a/designer-base/src/main/java/com/fr/design/gui/ibutton/UIButton.java +++ b/designer-base/src/main/java/com/fr/design/gui/ibutton/UIButton.java @@ -1,6 +1,7 @@ package com.fr.design.gui.ibutton; import com.fanruan.gui.UiInspector; +import com.fine.theme.icon.LazyIcon; import com.fine.theme.light.ui.laf.FineLightLaf; import com.fr.base.BaseUtils; import com.fr.base.CellBorderStyle; @@ -74,6 +75,9 @@ public class UIButton extends JButton implements UIObserver, UITextComponent { public UIButton(Icon icon) { super(icon); + if (icon instanceof LazyIcon) { + this.setDisabledIcon(((LazyIcon) icon).disabled()); + } init(); } diff --git a/designer-base/src/main/java/com/fr/design/gui/ibutton/UIHeadGroup.java b/designer-base/src/main/java/com/fr/design/gui/ibutton/UIHeadGroup.java index 8d92c8e5f2..1eeb0bb913 100644 --- a/designer-base/src/main/java/com/fr/design/gui/ibutton/UIHeadGroup.java +++ b/designer-base/src/main/java/com/fr/design/gui/ibutton/UIHeadGroup.java @@ -2,7 +2,7 @@ package com.fr.design.gui.ibutton; import com.fine.swing.ui.layout.Layouts; import com.fine.swing.ui.layout.Row; -import com.fine.theme.light.utils.FineUIUtils; +import com.fine.theme.utils.FineUIUtils; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.util.ScaledEmptyBorder; diff --git a/designer-base/src/main/java/com/fr/design/gui/ibutton/UIToggleButton.java b/designer-base/src/main/java/com/fr/design/gui/ibutton/UIToggleButton.java index 18764e9119..afa0643c97 100644 --- a/designer-base/src/main/java/com/fr/design/gui/ibutton/UIToggleButton.java +++ b/designer-base/src/main/java/com/fr/design/gui/ibutton/UIToggleButton.java @@ -1,5 +1,15 @@ package com.fr.design.gui.ibutton; +import com.fr.design.constants.UIConstants; +import com.fr.design.event.GlobalNameListener; +import com.fr.design.event.GlobalNameObserver; +import com.fr.stable.StringUtils; + +import javax.swing.AbstractAction; +import javax.swing.Icon; +import javax.swing.SwingUtilities; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Shape; @@ -9,14 +19,6 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.geom.RoundRectangle2D; -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import com.fr.design.constants.UIConstants; -import com.fr.design.event.GlobalNameListener; -import com.fr.design.event.GlobalNameObserver; -import com.fr.stable.StringUtils; /** * SelectedAble button label @@ -125,22 +127,22 @@ public class UIToggleButton extends UIButton implements GlobalNameObserver{ } @Override - protected void initListener(){ - if(shouldResponseChangeListener()){ - this.addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - if (uiObserverListener == null) { - return; - } + protected void initListener(){ + if(shouldResponseChangeListener()){ + this.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + if (uiObserverListener == null) { + return; + } if(globalNameListener!=null && shouldResponseNameListener()){ globalNameListener.setGlobalName(toggleButtonName); } - uiObserverListener.doChange(); - } - }); - } - } + uiObserverListener.doChange(); + } + }); + } + } public void setSelected(boolean isSelected, boolean fireChanged) { if (this.isSelected != isSelected) { @@ -243,7 +245,7 @@ public class UIToggleButton extends UIButton implements GlobalNameObserver{ */ @Override public void registerNameListener(GlobalNameListener listener) { - globalNameListener = listener; + globalNameListener = listener; } /** diff --git a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java index 76abb74455..e3742f8a31 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java @@ -1,5 +1,6 @@ package com.fr.design.mainframe; +import com.fine.theme.icon.LazyIcon; import com.fr.base.Parameter; import com.fr.base.TRL; import com.fr.base.extension.FileExtension; @@ -1593,7 +1594,7 @@ public abstract class JTemplate> } protected UIButton createTemplateThemeButton() { - UIButton button = new UIButton(IconUtils.readIcon("/com/fr/design/standard/template_theme")) { + UIButton button = new UIButton(new LazyIcon("template_theme")) { @Override public Dimension getPreferredSize() { FontMetrics metrics = getFontMetrics(getFont()); diff --git a/designer-base/src/main/java/com/fr/design/mainframe/check/CheckButton.java b/designer-base/src/main/java/com/fr/design/mainframe/check/CheckButton.java index 1d09f24828..dc502dcdf4 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/check/CheckButton.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/check/CheckButton.java @@ -1,7 +1,7 @@ package com.fr.design.mainframe.check; +import com.fine.theme.icon.LazyIcon; import com.fr.base.BaseUtils; -import com.fr.base.svg.IconUtils; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ilable.UILabel; @@ -45,7 +45,7 @@ public class CheckButton extends UIButton { private UILabel imageLabel; public CheckButton() { - this.setIcon(IconUtils.readIcon("/com/fr/design/standard/font_miss_check")); + this.setIcon(new LazyIcon("font_miss_check")); this.setToolTipText(Toolkit.i18nText("Fine_Designer_Check_Font")); this.set4ToolbarButton(); this.addActionListener(checkListener); diff --git a/designer-base/src/main/resources/com/fine/theme/icon/copy.svg b/designer-base/src/main/resources/com/fine/theme/icon/copy.svg new file mode 100644 index 0000000000..160be86092 --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/copy.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/copy_disable.svg b/designer-base/src/main/resources/com/fine/theme/icon/copy_disable.svg new file mode 100644 index 0000000000..786ba17b82 --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/copy_disable.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/cut.svg b/designer-base/src/main/resources/com/fine/theme/icon/cut.svg new file mode 100644 index 0000000000..6cb12c56e8 --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/cut.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/cut_disable.svg b/designer-base/src/main/resources/com/fine/theme/icon/cut_disable.svg new file mode 100644 index 0000000000..eb27ced476 --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/cut_disable.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/font_miss_check.svg b/designer-base/src/main/resources/com/fine/theme/icon/font_miss_check.svg new file mode 100644 index 0000000000..46125afd25 --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/font_miss_check.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/font_miss_check_disable.svg b/designer-base/src/main/resources/com/fine/theme/icon/font_miss_check_disable.svg new file mode 100644 index 0000000000..0bde37db4c --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/font_miss_check_disable.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/formatBrush.svg b/designer-base/src/main/resources/com/fine/theme/icon/formatBrush.svg new file mode 100644 index 0000000000..c1531fbed4 --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/formatBrush.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/formatBrush_disable.svg b/designer-base/src/main/resources/com/fine/theme/icon/formatBrush_disable.svg new file mode 100644 index 0000000000..f732f5b64d --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/formatBrush_disable.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/paste.svg b/designer-base/src/main/resources/com/fine/theme/icon/paste.svg new file mode 100644 index 0000000000..d520164fb6 --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/paste.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/paste_disable.svg b/designer-base/src/main/resources/com/fine/theme/icon/paste_disable.svg new file mode 100644 index 0000000000..076509580d --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/paste_disable.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/redo.svg b/designer-base/src/main/resources/com/fine/theme/icon/redo.svg new file mode 100644 index 0000000000..5fa4361cfe --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/redo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/redo_disable.svg b/designer-base/src/main/resources/com/fine/theme/icon/redo_disable.svg new file mode 100644 index 0000000000..42adaa3271 --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/redo_disable.svg @@ -0,0 +1,4 @@ + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/save.svg b/designer-base/src/main/resources/com/fine/theme/icon/save.svg new file mode 100644 index 0000000000..469a1a61b0 --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/save.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/save_disable.svg b/designer-base/src/main/resources/com/fine/theme/icon/save_disable.svg new file mode 100644 index 0000000000..1085e2bb0c --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/save_disable.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/template_theme.svg b/designer-base/src/main/resources/com/fine/theme/icon/template_theme.svg new file mode 100644 index 0000000000..48a11990d9 --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/template_theme.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/template_theme_disable.svg b/designer-base/src/main/resources/com/fine/theme/icon/template_theme_disable.svg new file mode 100644 index 0000000000..94a01fa74d --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/template_theme_disable.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/undo.svg b/designer-base/src/main/resources/com/fine/theme/icon/undo.svg new file mode 100644 index 0000000000..6a76ee0b67 --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/undo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/undo_disable.svg b/designer-base/src/main/resources/com/fine/theme/icon/undo_disable.svg new file mode 100644 index 0000000000..4e186ac0a7 --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/undo_disable.svg @@ -0,0 +1,4 @@ + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/version_save.svg b/designer-base/src/main/resources/com/fine/theme/icon/version_save.svg new file mode 100644 index 0000000000..3e7f6e1141 --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/version_save.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/icon/version_save_disable.svg b/designer-base/src/main/resources/com/fine/theme/icon/version_save_disable.svg new file mode 100644 index 0000000000..4251f6a911 --- /dev/null +++ b/designer-base/src/main/resources/com/fine/theme/icon/version_save_disable.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/designer-base/src/main/resources/com/fine/theme/light/ui/laf/FineLightLaf.properties b/designer-base/src/main/resources/com/fine/theme/light/ui/laf/FineLightLaf.properties index 02700bd9f4..b35df4f71f 100644 --- a/designer-base/src/main/resources/com/fine/theme/light/ui/laf/FineLightLaf.properties +++ b/designer-base/src/main/resources/com/fine/theme/light/ui/laf/FineLightLaf.properties @@ -30,6 +30,17 @@ # which is licensed under the Apache 2.0 license. Copyright 2000-2019 JetBrains s.r.o. # See: https://github.com/JetBrains/intellij-community/ +# font weights +# Windows +[win]light.font = "Microsoft YaHei", "Microsoft JhengHei", "MingLiU", "Arial" +[win]semibold.font = "Microsoft YaHei", "Microsoft JhengHei", "MingLiU", "Arial" +# macOS +[mac]light.font = "PingFang SC", "Apple LiGothic", "Apple LiSun", "Arial" +[mac]semibold.font = "PingFang SC", "Apple LiGothic", "Apple LiSun", "Arial" +# Linux +[linux]light.font = "Noto SansCJK", "SimHei", "Arial", "Ubuntu" +[linux]semibold.font = "Noto SansCJK", "SimHei", "Arial", "Ubuntu" + #---- UI delegates ---- ButtonUI = com.formdev.flatlaf.ui.FlatButtonUI @@ -107,6 +118,8 @@ ViewportUI = com.formdev.flatlaf.ui.FlatViewportUI @cellFocusColor = darken(@selectionBackground,20%) @icon = shade(@background,27%) + + # accent colors (blueish) # set @accentColor to use single accent color or # modify @accentBaseColor to use variations of accent base color @@ -783,7 +796,7 @@ TabbedPane.closeCrossLineWidth = 1 TabbedPane.underlineColor = @accentUnderlineColor TabbedPane.inactiveUnderlineColor = mix(@accentUnderlineColor,$TabbedPane.background,50%) TabbedPane.disabledUnderlineColor = darken(@background,28%) -TabbedPane.hoverColor = darken($TabbedPane.background,7%,derived) +TabbedPane.hoverColor = #DADEE7 TabbedPane.focusColor = mix(@selectionBackground,$TabbedPane.background,10%) TabbedPane.contentAreaColor = $Component.borderColor diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/FormatBrushAction.java b/designer-realize/src/main/java/com/fr/design/mainframe/FormatBrushAction.java index d0ad191296..586b6944c9 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/FormatBrushAction.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/FormatBrushAction.java @@ -1,5 +1,6 @@ package com.fr.design.mainframe; +import com.fine.theme.icon.LazyIcon; import com.fr.base.Style; import com.fr.design.actions.ElementCaseAction; @@ -34,7 +35,7 @@ public class FormatBrushAction extends ElementCaseAction { super(t); this.setName(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_M_Edit_FormatBrush")); this.setMnemonic('B'); - this.setSmallIcon("/com/fr/design/standard/formatbrush/formatBrush", false); + this.setSmallIcon(new LazyIcon("formatBrush")); this.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, DEFAULT_MODIFIER)); } diff --git a/designer-realize/src/main/java/com/fr/start/MainDesigner.java b/designer-realize/src/main/java/com/fr/start/MainDesigner.java index f02b061343..f733d64eb8 100644 --- a/designer-realize/src/main/java/com/fr/start/MainDesigner.java +++ b/designer-realize/src/main/java/com/fr/start/MainDesigner.java @@ -2,6 +2,7 @@ package com.fr.start; import com.fanruan.gui.UiInspector; +import com.fine.theme.icon.LazyIcon; import com.fr.base.function.UITerminator; import com.fr.base.vcs.DesignerMode; import com.fr.design.DesignerEnvManager; @@ -312,7 +313,7 @@ public class MainDesigner extends BaseDesigner { private void createSaveButton() { - saveButton = new UIButton("/com/fr/design/standard/save/save", true); + saveButton = new UIButton(new LazyIcon("save"), true); saveButton.setToolTipText(KeySetUtils.SAVE_TEMPLATE.getMenuKeySetName()); saveButton.set4ToolbarButton(); saveButton.addActionListener(new ActionListener() { @@ -329,7 +330,7 @@ public class MainDesigner extends BaseDesigner { private void createUndoButton() { - undo = new UIButton("/com/fr/design/standard/undo/undo", true); + undo = new UIButton(new LazyIcon("undo"), true); undo.setToolTipText(KeySetUtils.UNDO.getMenuKeySetName()); undo.set4ToolbarButton(); undo.addActionListener(new ActionListener() { @@ -344,7 +345,7 @@ public class MainDesigner extends BaseDesigner { } private void createRedoButton() { - redo = new UIButton("/com/fr/design/standard/redo/redo", true); + redo = new UIButton(new LazyIcon("redo"), true); redo.setToolTipText(KeySetUtils.REDO.getMenuKeySetName()); redo.set4ToolbarButton(); redo.addActionListener(new ActionListener() {