Merge in DESIGN/design from ~VITO/c-design:newui to newui * commit 'bee1e0a6b993e3bac25685eefeb50a8e489f29d7': 无jira任务 代码质量 REPORT-99485 新图标管理器newui
@ -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<String, IconSource<? extends Icon>> map = new ConcurrentHashMap<>(64); |
||||||
|
private final Map<String, Icon> cache = new ConcurrentHashMap<>(64); |
||||||
|
private final Map<String, Icon> disableCache = new ConcurrentHashMap<>(64); |
||||||
|
|
||||||
|
public AbstractIconSet(String id) { |
||||||
|
this.id = id; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public @NotNull String getId() { |
||||||
|
return id; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public @NotNull Collection<String> getIds() { |
||||||
|
return map.keySet(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addIcon(@NotNull IconSource<? extends Icon> icon) { |
||||||
|
map.put(icon.getId(), icon); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addIcon(@NotNull IconSource<? extends Icon>... icons) { |
||||||
|
for (IconSource<? extends Icon> icon: icons){ |
||||||
|
map.put(icon.getId(), icon); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public @Nullable Icon findIcon(@NotNull String id) { |
||||||
|
Icon icon = cache.get(id); |
||||||
|
if (icon == null) { |
||||||
|
IconSource<? extends Icon> 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<? extends Icon> iconSource = map.get(id); |
||||||
|
if (iconSource != null) { |
||||||
|
icon = iconSource.disabled(); |
||||||
|
disableCache.put(id, icon); |
||||||
|
} |
||||||
|
} |
||||||
|
return icon; |
||||||
|
} |
||||||
|
} |
@ -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<I extends Icon> implements IconSource<I> { |
||||||
|
|
||||||
|
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(); |
||||||
|
} |
@ -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(); |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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<IconSet> iconSets = new ArrayList<>(2); |
||||||
|
; |
||||||
|
public static HashMap<String, WeakReference<Icon>> cache = new HashMap<>(60); |
||||||
|
; |
||||||
|
public static HashMap<String, WeakReference<Icon>> 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 <I> 图标类型 |
||||||
|
* @return 图标 |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public static <I extends Icon> 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 <I> 图标类型 |
||||||
|
* @return 图标 |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
public static <I extends Icon> 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 extends Icon> I findDisableIcon(String id) { |
||||||
|
final WeakReference<Icon> 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 extends Icon> I findIcon(String id) { |
||||||
|
final WeakReference<Icon> 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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
@ -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<String> getIds(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 将指定 IconSource 引用的新 Icon 添加到集合中。 |
||||||
|
* |
||||||
|
* @param icon icon 源 |
||||||
|
*/ |
||||||
|
void addIcon(@NotNull IconSource<? extends Icon> icon); |
||||||
|
|
||||||
|
/** |
||||||
|
* 将指定 IconSource 引用的新 Icon 添加到集合中。 |
||||||
|
* |
||||||
|
* @param icon icon 源 |
||||||
|
*/ |
||||||
|
void addIcon(@NotNull IconSource<? extends Icon>... 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); |
||||||
|
} |
@ -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<I extends Icon> extends Identifiable, DisabledIcon, Cloneable, Serializable { |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取图标资源 |
||||||
|
* |
||||||
|
* @return 图标资源 |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
IconResource getResource(); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 加载图标 |
||||||
|
* |
||||||
|
* @return 图标 |
||||||
|
*/ |
||||||
|
@NotNull |
||||||
|
I loadIcon(); |
||||||
|
} |
@ -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(); |
||||||
|
} |
@ -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 extends Icon> 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() + "]"; |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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<SvgIcon> { |
||||||
|
|
||||||
|
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(); |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -1,11 +0,0 @@ |
|||||||
package com.fine.theme.light.icon; |
|
||||||
|
|
||||||
/** |
|
||||||
* 图标管理器 |
|
||||||
* |
|
||||||
* @author vito |
|
||||||
* @since 11.0 |
|
||||||
* Created on 2023/9/12 |
|
||||||
*/ |
|
||||||
public class IconManager { |
|
||||||
} |
|
@ -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) |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -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); |
|
||||||
} |
|
||||||
} |
|
@ -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". |
||||||
|
* <p> |
||||||
|
* 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. |
||||||
|
* <p> |
||||||
|
* 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. |
||||||
|
* <p> |
||||||
|
* 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); |
||||||
|
} |
||||||
|
} |
@ -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<Boolean> 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); |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.6 KiB |