vito-刘恒霖
10 months ago
702 changed files with 15629 additions and 6552 deletions
@ -0,0 +1,88 @@
|
||||
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 findWhiteIcon(@NotNull String id) { |
||||
IconSource<? extends Icon> iconSource = map.get(id); |
||||
if (iconSource != null) { |
||||
return iconSource.white(); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
@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,59 @@
|
||||
package com.fine.theme.icon; |
||||
|
||||
import com.formdev.flatlaf.util.Graphics2DProxy; |
||||
|
||||
import java.awt.Color; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.Paint; |
||||
import java.awt.image.RGBImageFilter; |
||||
|
||||
/** |
||||
* 颜色过滤器画板 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/11/21 |
||||
*/ |
||||
public class GraphicsFilter |
||||
extends Graphics2DProxy { |
||||
private final RGBImageFilter grayFilter; |
||||
|
||||
public GraphicsFilter(Graphics2D delegate, RGBImageFilter grayFilter) { |
||||
super(delegate); |
||||
this.grayFilter = grayFilter; |
||||
} |
||||
|
||||
@Override |
||||
public Graphics create() { |
||||
return new GraphicsFilter((Graphics2D) super.create(), grayFilter); |
||||
} |
||||
|
||||
@Override |
||||
public Graphics create(int x, int y, int width, int height) { |
||||
return new GraphicsFilter((Graphics2D) super.create(x, y, width, height), grayFilter); |
||||
} |
||||
|
||||
@Override |
||||
public void setColor(Color c) { |
||||
super.setColor(filterColor(c)); |
||||
} |
||||
|
||||
@Override |
||||
public void setPaint(Paint paint) { |
||||
if (paint instanceof Color) { |
||||
paint = filterColor((Color) paint); |
||||
} |
||||
super.setPaint(paint); |
||||
} |
||||
|
||||
private Color filterColor(Color color) { |
||||
|
||||
if (grayFilter != null) { |
||||
int oldRGB = color.getRGB(); |
||||
int newRGB = grayFilter.filterRGB(0, 0, oldRGB); |
||||
color = (newRGB != oldRGB) ? new Color(newRGB, true) : color; |
||||
} |
||||
return color; |
||||
} |
||||
} |
@ -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,175 @@
|
||||
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<>(64); |
||||
public static HashMap<String, WeakReference<Icon>> disableCache = new HashMap<>(64); |
||||
public static HashMap<String, WeakReference<Icon>> whiteCache = new HashMap<>(32); |
||||
|
||||
|
||||
/** |
||||
* 获取图标集 |
||||
* |
||||
* @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 getWhiteIcon(String id) { |
||||
final WeakReference<Icon> reference = whiteCache.get(id); |
||||
I icon = reference != null ? (I) reference.get() : null; |
||||
if (icon == null) { |
||||
for (IconSet set : iconSets) { |
||||
Icon f = set.findWhiteIcon(id); |
||||
if (f != null) { |
||||
icon = (I) f; |
||||
whiteCache.put(id, new WeakReference<>(icon)); |
||||
} |
||||
} |
||||
} |
||||
if (icon == null) { |
||||
throw new IconException("[IconManager] Can not find icon by id: " + id); |
||||
} |
||||
return 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(); |
||||
disableCache.clear(); |
||||
} |
||||
|
||||
/** |
||||
* 清理图标缓存 |
||||
*/ |
||||
public static void clearIconCache(String id) { |
||||
cache.remove(id); |
||||
disableCache.remove(id); |
||||
} |
||||
|
||||
private static void clearIconSetCache(@NotNull IconSet set) { |
||||
for (String id : set.getIds()) { |
||||
cache.remove(id); |
||||
disableCache.remove(id); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,23 @@
|
||||
package com.fine.theme.icon; |
||||
|
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.io.InputStream; |
||||
|
||||
/** |
||||
* 资源接口 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/11/6 |
||||
*/ |
||||
public interface IconResource { |
||||
|
||||
/** |
||||
* 获取输入资源流 |
||||
* |
||||
* @return 资源流 |
||||
*/ |
||||
@NotNull |
||||
InputStream getInputStream(); |
||||
} |
@ -0,0 +1,71 @@
|
||||
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 findWhiteIcon(@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, WhiteIcon, 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,83 @@
|
||||
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, WhiteIcon, 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()); |
||||
} |
||||
|
||||
/** |
||||
* 创建一份白化图标 |
||||
* |
||||
* @return 白化图标 |
||||
*/ |
||||
@NotNull |
||||
@Override |
||||
public Icon white() { |
||||
return IconManager.getWhiteIcon(getId()); |
||||
} |
||||
|
||||
|
||||
@NotNull |
||||
@Override |
||||
public String toString() { |
||||
return getClass().getCanonicalName() + "[id=" + getId() + "]"; |
||||
|
||||
} |
||||
} |
@ -0,0 +1,53 @@
|
||||
package com.fine.theme.icon; |
||||
|
||||
import com.fr.general.IOUtils; |
||||
import com.fr.io.utils.ResourceIOUtils; |
||||
import com.fr.third.errorprone.annotations.Immutable; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.io.InputStream; |
||||
import java.util.StringJoiner; |
||||
|
||||
/** |
||||
* 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 |
||||
@NotNull |
||||
public InputStream getInputStream() { |
||||
InputStream inputStream = getInputStream(path); |
||||
if (inputStream == null) { |
||||
throw new IconException("Icon load failed: " + path); |
||||
} |
||||
return inputStream; |
||||
} |
||||
|
||||
|
||||
private InputStream getInputStream(String path) { |
||||
InputStream inputStream = IOUtils.getInputStream(path); |
||||
return inputStream != null ? inputStream : ResourceIOUtils.read(path); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return new StringJoiner(", ", UrlIconResource.class.getSimpleName() + "[", "]") |
||||
.add("path='" + path + "'") |
||||
.toString(); |
||||
} |
||||
} |
@ -0,0 +1,22 @@
|
||||
package com.fine.theme.icon; |
||||
|
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.Icon; |
||||
|
||||
/** |
||||
* 白化图像 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2024/1/8 |
||||
*/ |
||||
public interface WhiteIcon { |
||||
/** |
||||
* 创建一份白化图标 |
||||
* |
||||
* @return 灰化图标 |
||||
*/ |
||||
@NotNull |
||||
Icon white(); |
||||
} |
@ -0,0 +1,75 @@
|
||||
package com.fine.theme.icon.icons; |
||||
|
||||
import com.formdev.flatlaf.icons.FlatAnimatedIcon; |
||||
import com.formdev.flatlaf.util.ColorFunctions; |
||||
import org.jetbrains.annotations.Nullable; |
||||
|
||||
import javax.swing.AbstractButton; |
||||
import javax.swing.ButtonModel; |
||||
import javax.swing.JRadioButton; |
||||
import javax.swing.UIManager; |
||||
import java.awt.Color; |
||||
import java.awt.Component; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.geom.Ellipse2D; |
||||
|
||||
/** |
||||
* RadioButton 图标,带动画 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/11/22 |
||||
*/ |
||||
public class AnimatedRadioButtonIcon |
||||
extends FlatAnimatedIcon { |
||||
private static final int SIZE = 16; |
||||
private static final int BORDER_SIZE = 2; |
||||
private static final int ON_SIZE = 8; |
||||
|
||||
private final Color offColor = UIManager.getColor("CheckBox.icon.borderColor"); |
||||
private final Color onColor = UIManager.getColor("CheckBox.icon.checkmarkColor"); |
||||
|
||||
public AnimatedRadioButtonIcon() { |
||||
super(SIZE, SIZE, null); |
||||
} |
||||
|
||||
@Override |
||||
public void paintIconAnimated(Component c, Graphics g, int x, int y, float animatedValue) { |
||||
Color color = ColorFunctions.mix(onColor, offColor, animatedValue); |
||||
|
||||
// border
|
||||
g.setColor(getBorderColor(c, color, onColor)); |
||||
g.fillOval(0, 0, SIZE, SIZE); |
||||
|
||||
// background
|
||||
g.setColor(c.getBackground()); |
||||
float onDiameter = SIZE - (BORDER_SIZE + (ON_SIZE - BORDER_SIZE) * (animatedValue)); |
||||
float xy = (SIZE - onDiameter) / 2f; |
||||
((Graphics2D) g).fill(new Ellipse2D.Float(xy, xy, onDiameter, onDiameter)); |
||||
|
||||
} |
||||
|
||||
@Nullable |
||||
private Color getBorderColor(Component c, Color enableColor, Color hoverColor) { |
||||
if (c instanceof AbstractButton) { |
||||
ButtonModel model = ((AbstractButton) c).getModel(); |
||||
|
||||
if (model.isRollover()) { |
||||
return hoverColor; |
||||
} |
||||
} |
||||
return enableColor; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public float getValue(Component c) { |
||||
return ((JRadioButton) c).isSelected() ? 1 : 0; |
||||
} |
||||
|
||||
@Override |
||||
public int getAnimationDuration() { |
||||
return 200; |
||||
} |
||||
} |
@ -0,0 +1,163 @@
|
||||
package com.fine.theme.icon.svg; |
||||
|
||||
import com.fine.theme.icon.DisabledIcon; |
||||
import com.fine.theme.icon.GraphicsFilter; |
||||
import com.fine.theme.icon.IconResource; |
||||
import com.fine.theme.icon.WhiteIcon; |
||||
import com.formdev.flatlaf.FlatLaf; |
||||
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||
import com.formdev.flatlaf.util.GrayFilter; |
||||
import com.fr.clone.cloning.Immutable; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.value.NullableLazyValue; |
||||
import com.github.weisj.jsvg.SVGDocument; |
||||
import com.github.weisj.jsvg.attributes.ViewBox; |
||||
import com.github.weisj.jsvg.parser.SVGLoader; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.Icon; |
||||
import javax.swing.JComponent; |
||||
import javax.swing.UIManager; |
||||
import java.awt.Component; |
||||
import java.awt.Dimension; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.image.RGBImageFilter; |
||||
import java.util.Objects; |
||||
import java.util.StringJoiner; |
||||
|
||||
import static com.fine.theme.utils.FineUIScale.scale; |
||||
|
||||
/** |
||||
* svg图标 |
||||
* 1.绘制长度会跟随DPI比率变化 |
||||
* 1跟2的缩放原因不同,因此不能混淆,宽高测量等依旧 |
||||
* 使用DPI缩放进行,只有绘制内容时使用Retina绘制(如果有) |
||||
* Retina绘制不影响最终尺寸,注意区分 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/11/15 |
||||
*/ |
||||
@Immutable |
||||
public class SvgIcon implements DisabledIcon, WhiteIcon, Icon { |
||||
|
||||
public enum Type { |
||||
/** |
||||
* 灰化图 |
||||
*/ |
||||
disable, |
||||
/** |
||||
* 白化图,用于反白场景 |
||||
*/ |
||||
white, |
||||
/** |
||||
* 原始效果图 |
||||
*/ |
||||
origin |
||||
} |
||||
|
||||
private final Dimension size; |
||||
private final IconResource resource; |
||||
private final Type type; |
||||
|
||||
private final NullableLazyValue<SVGDocument> svgDocument = NullableLazyValue.createValue(() -> load(Type.origin)); |
||||
private final NullableLazyValue<SVGDocument> whiteSvgDocument = NullableLazyValue.createValue(() -> load(Type.white)); |
||||
|
||||
public SvgIcon(IconResource resource, Dimension size) { |
||||
this(resource, size, Type.origin); |
||||
} |
||||
|
||||
public SvgIcon(IconResource resource, Dimension size, Type type) { |
||||
this.resource = resource; |
||||
// 根据dpi进行缩放
|
||||
this.size = scale(size); |
||||
this.type = type; |
||||
} |
||||
|
||||
public SvgIcon(IconResource resource, int side) { |
||||
this(resource, new Dimension(side, side), Type.origin); |
||||
} |
||||
|
||||
/** |
||||
* 如果支持绘制Retina绘制,则进行Retina绘制, |
||||
* 绘制结束不影响任何外部尺寸 |
||||
*/ |
||||
@Override |
||||
public void paintIcon(Component c, Graphics g, int x, int y) { |
||||
if (type == Type.disable) { |
||||
g = grayGraphics(g); |
||||
} |
||||
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints(g); |
||||
render(c, g, x, y); |
||||
FlatUIUtils.resetRenderingHints(g, oldRenderingHints); |
||||
} |
||||
|
||||
private Graphics2D grayGraphics(Graphics g) { |
||||
Object grayFilterObj = UIManager.get("Component.grayFilter"); |
||||
RGBImageFilter grayFilter = (grayFilterObj instanceof RGBImageFilter) |
||||
? (RGBImageFilter) grayFilterObj |
||||
: GrayFilter.createDisabledIconFilter(FlatLaf.isLafDark()); |
||||
|
||||
return new GraphicsFilter((Graphics2D) g.create(), grayFilter); |
||||
} |
||||
|
||||
@Override |
||||
public int getIconWidth() { |
||||
return size.width; |
||||
} |
||||
|
||||
@Override |
||||
public int getIconHeight() { |
||||
return size.height; |
||||
} |
||||
|
||||
private void render(Component c, Graphics g, int x, int y) { |
||||
try { |
||||
if (type == Type.white) { |
||||
Objects.requireNonNull(whiteSvgDocument.getValue()) |
||||
.render((JComponent) c, (Graphics2D) g, new ViewBox(x, y, size.width, size.height)); |
||||
} else { |
||||
Objects.requireNonNull(svgDocument.getValue()) |
||||
.render((JComponent) c, (Graphics2D) g, new ViewBox(x, y, size.width, size.height)); |
||||
} |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error("SvgIcon from url: " + resource + "can not paint.", e); |
||||
} |
||||
} |
||||
|
||||
|
||||
private SVGDocument load(Type type) { |
||||
SVGLoader loader = new SVGLoader(); |
||||
return type == Type.white |
||||
? loader.load(resource.getInputStream(), new WhiteParser()) |
||||
: loader.load(resource.getInputStream()); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public String toString() { |
||||
return new StringJoiner(", ", SvgIcon.class.getSimpleName() + "[", "]") |
||||
.add("resource=" + resource) |
||||
.add("type=" + type) |
||||
.add("size=" + size) |
||||
.toString(); |
||||
} |
||||
|
||||
/** |
||||
* 默认提供一个简单的灰化处理 |
||||
*/ |
||||
@Override |
||||
public @NotNull SvgIcon white() { |
||||
return new SvgIcon(resource, size, Type.white); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 默认提供一个简单的灰化处理 |
||||
*/ |
||||
@Override |
||||
public @NotNull SvgIcon disabled() { |
||||
return new SvgIcon(resource, size, Type.disable); |
||||
} |
||||
} |
@ -0,0 +1,80 @@
|
||||
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 javax.swing.Icon; |
||||
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(); |
||||
} |
||||
|
||||
@Override |
||||
public @NotNull Icon white() { |
||||
return new SvgIcon(resource, size).white(); |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -0,0 +1,50 @@
|
||||
package com.fine.theme.icon.svg; |
||||
|
||||
import com.github.weisj.jsvg.attributes.paint.AwtSVGPaint; |
||||
import com.github.weisj.jsvg.attributes.paint.PaintParser; |
||||
import com.github.weisj.jsvg.attributes.paint.SVGPaint; |
||||
import com.github.weisj.jsvg.parser.AttributeNode; |
||||
import com.github.weisj.jsvg.parser.DefaultParserProvider; |
||||
import org.jetbrains.annotations.NotNull; |
||||
import org.jetbrains.annotations.Nullable; |
||||
|
||||
import java.awt.Color; |
||||
|
||||
/** |
||||
* svg绘制白化转化器 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2024/1/8 |
||||
*/ |
||||
public class WhiteParser extends DefaultParserProvider { |
||||
@Override |
||||
public @NotNull PaintParser createPaintParser() { |
||||
return new WhitePaintParser(super.createPaintParser()); |
||||
} |
||||
|
||||
|
||||
static class WhitePaintParser implements PaintParser { |
||||
|
||||
private final PaintParser delegate; |
||||
|
||||
WhitePaintParser(PaintParser delegate) { |
||||
this.delegate = delegate; |
||||
} |
||||
|
||||
@Override |
||||
public @Nullable Color parseColor(@NotNull String value, @NotNull AttributeNode attributeNode) { |
||||
return delegate.parseColor(value, attributeNode); |
||||
} |
||||
|
||||
@Override |
||||
public @Nullable SVGPaint parsePaint(@Nullable String value, @NotNull AttributeNode attributeNode) { |
||||
SVGPaint paint = delegate.parsePaint(value, attributeNode); |
||||
if (!(paint instanceof AwtSVGPaint)) { |
||||
return paint; |
||||
} |
||||
return new AwtSVGPaint(Color.WHITE); |
||||
} |
||||
} |
||||
} |
||||
|
@ -0,0 +1,42 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatButtonBorder; |
||||
|
||||
import java.awt.Component; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.Paint; |
||||
|
||||
import static com.fine.theme.light.ui.FineButtonUI.isPartRoundButton; |
||||
|
||||
/** |
||||
* 按钮边框 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/12/20 |
||||
*/ |
||||
public class FineButtonBorder extends FlatButtonBorder { |
||||
|
||||
public FineButtonBorder() { |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { |
||||
if (isPartRoundButton(c)) { |
||||
Graphics2D g2 = (Graphics2D) g.create(); |
||||
Paint borderPaint = getBorderColor(c); |
||||
if (borderPaint == null) { |
||||
return; |
||||
} |
||||
g2.setPaint(borderPaint); |
||||
FineUIUtils.paintPartRoundButtonBorder(c, g2, x, y, width, height, borderWidth, (float) getArc(c)); |
||||
} else { |
||||
super.paintBorder(c, g, x, y, width, height); |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,42 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fr.design.gui.ibutton.UIButtonGroup; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import javax.swing.plaf.PanelUI; |
||||
import java.awt.Graphics; |
||||
|
||||
/** |
||||
* 按钮组UI,应用于 {@link com.fr.design.gui.ibutton.UIButtonGroup} |
||||
* |
||||
* @author Levy.Xie |
||||
* @since 11.0 |
||||
* Created on 2023/12/15 |
||||
*/ |
||||
public class FineButtonGroupUI extends PanelUI { |
||||
|
||||
/** |
||||
* 创建UI |
||||
* |
||||
* @param c 组件 |
||||
* @return ComponentUI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineButtonGroupUI(); |
||||
} |
||||
|
||||
@Override |
||||
public void update(Graphics g, JComponent c) { |
||||
UIButtonGroup group = (UIButtonGroup) c; |
||||
if (!group.isInToolbar()) { |
||||
c.setBorder(new FineRoundBorder()); |
||||
} |
||||
super.update(g, c); |
||||
} |
||||
|
||||
@Override |
||||
public void uninstallUI(JComponent c) { |
||||
super.uninstallUI(c); |
||||
} |
||||
} |
@ -0,0 +1,114 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineClientProperties; |
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatButtonUI; |
||||
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||
|
||||
import javax.swing.AbstractButton; |
||||
import javax.swing.JButton; |
||||
import javax.swing.JComponent; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.Color; |
||||
import java.awt.Component; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.geom.Path2D; |
||||
|
||||
import static com.fine.theme.utils.FineClientProperties.BUTTON_BORDER; |
||||
|
||||
/** |
||||
* 按钮UI |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/12/20 |
||||
*/ |
||||
public class FineButtonUI extends FlatButtonUI { |
||||
|
||||
/** |
||||
* @param shared |
||||
* @since 2 |
||||
*/ |
||||
protected FineButtonUI(boolean shared) { |
||||
super(shared); |
||||
} |
||||
|
||||
/** |
||||
* 是否左圆角矩形 |
||||
* |
||||
* @param c 组件 |
||||
* @return 是否左圆角矩形 |
||||
*/ |
||||
public static boolean isLeftRoundButton(Component c) { |
||||
return c instanceof JButton |
||||
&& FineClientProperties.BUTTON_BORDER_LEFT_ROUND_RECT.equals(getButtonBorderTypeStr((JButton) c)); |
||||
} |
||||
|
||||
/** |
||||
* 是否右圆角矩形 |
||||
* |
||||
* @param c 组件 |
||||
* @return 是否右圆角矩形 |
||||
*/ |
||||
public static boolean isRightRoundButton(Component c) { |
||||
return c instanceof JButton |
||||
&& FineClientProperties.BUTTON_BORDER_RIGHT_ROUND_RECT.equals(getButtonBorderTypeStr((JButton) c)); |
||||
} |
||||
|
||||
/** |
||||
* 是否部分圆角矩形 |
||||
* |
||||
* @param c 组件 |
||||
* @return 是否部分圆角矩形 |
||||
*/ |
||||
public static boolean isPartRoundButton(Component c) { |
||||
return isLeftRoundButton(c) || isRightRoundButton(c); |
||||
} |
||||
|
||||
protected void paintBackground(Graphics g, JComponent c) { |
||||
if (isPartRoundButton(c)) { |
||||
Color background = getBackground(c); |
||||
if (background == null) { |
||||
return; |
||||
} |
||||
|
||||
Graphics2D g2 = (Graphics2D) g.create(); |
||||
try { |
||||
FlatUIUtils.setRenderingHints(g2); |
||||
float arc = FlatUIUtils.getBorderArc(c); |
||||
int width = c.getWidth(); |
||||
int height = c.getHeight(); |
||||
|
||||
g2.setColor(FlatUIUtils.deriveColor(background, getBackgroundBase(c, false))); |
||||
Path2D path2DLeft; |
||||
if (isLeftRoundButton(c)) { |
||||
path2DLeft = FineUIUtils.createLeftRoundRectangle(0, 0, width, height, arc); |
||||
} else { |
||||
path2DLeft = FineUIUtils.createRightRoundRectangle(0, 0, width, height, arc); |
||||
} |
||||
g2.fill(path2DLeft); |
||||
} finally { |
||||
g2.dispose(); |
||||
} |
||||
} else { |
||||
super.paintBackground(g, c); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 创建UI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineButtonUI(false); |
||||
} |
||||
|
||||
static String getButtonBorderTypeStr(AbstractButton c) { |
||||
Object value = c.getClientProperty(BUTTON_BORDER); |
||||
if (value instanceof String) { |
||||
return (String) value; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,37 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.icon.LazyIcon; |
||||
import com.formdev.flatlaf.ui.FlatCheckBoxUI; |
||||
|
||||
import javax.swing.AbstractButton; |
||||
import javax.swing.JComponent; |
||||
import javax.swing.plaf.ComponentUI; |
||||
|
||||
/** |
||||
* 提供 {@link javax.swing.JCheckBox} 的UI类 |
||||
* |
||||
* @author Levy.Xie |
||||
* @since 11.0 |
||||
* Created on 2023/12/14 |
||||
*/ |
||||
public class FineCheckBoxUI extends FlatCheckBoxUI { |
||||
|
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineCheckBoxUI(false); |
||||
} |
||||
|
||||
protected FineCheckBoxUI(boolean shared) { |
||||
super(shared); |
||||
} |
||||
|
||||
@Override |
||||
public void installDefaults(AbstractButton b) { |
||||
super.installDefaults(b); |
||||
b.setIcon(new LazyIcon("checkbox_unchecked")); |
||||
b.setSelectedIcon(new LazyIcon("checkbox_checked")); |
||||
b.setRolloverIcon(new LazyIcon("checkbox_hovered")); |
||||
b.setDisabledIcon(new LazyIcon("checkbox_unchecked").disabled()); |
||||
b.setDisabledSelectedIcon(new LazyIcon("checkbox_checked").disabled()); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,66 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||
import com.fr.base.Utils; |
||||
import com.fr.design.gui.ibutton.UIColorButton; |
||||
|
||||
import javax.swing.ButtonModel; |
||||
import javax.swing.JComponent; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.Color; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.Rectangle; |
||||
import java.awt.geom.RoundRectangle2D; |
||||
|
||||
import static com.fine.theme.utils.FineUIScale.scale; |
||||
|
||||
/** |
||||
* 颜色按钮 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2024/1/3 |
||||
*/ |
||||
public class FineColorButtonUI extends FineButtonUI { |
||||
|
||||
public static final float HEIGHT = 1.5f; |
||||
public static final float WIDTH = 13; |
||||
public static final float Y = 13.5f; |
||||
|
||||
/** |
||||
* @param shared |
||||
* @since 2 |
||||
*/ |
||||
protected FineColorButtonUI(boolean shared) { |
||||
super(shared); |
||||
} |
||||
|
||||
/** |
||||
* 创建UI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineColorButtonUI(false); |
||||
} |
||||
|
||||
@Override |
||||
protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect) { |
||||
super.paintIcon(g, c, iconRect); |
||||
UIColorButton b = (UIColorButton) c; |
||||
ButtonModel model = b.getModel(); |
||||
if (model.isEnabled()) { |
||||
g.setColor(b.getColor()); |
||||
} else { |
||||
g.setColor(new Color(Utils.filterRGB(b.getColor().getRGB(), 50))); |
||||
} |
||||
FlatUIUtils.setRenderingHints(g); |
||||
Graphics2D g2d = (Graphics2D) g; |
||||
float height = scale(HEIGHT); |
||||
float width = scale(WIDTH); |
||||
// 计算实际大小与icon区域大小的偏移,用于居中调整
|
||||
float offsetX = (iconRect.width - width) / 2.0f; |
||||
RoundRectangle2D.Float colorRect = new RoundRectangle2D.Float( |
||||
iconRect.x + offsetX, iconRect.y + scale(Y), width, height, height, height); |
||||
g2d.fill(colorRect); |
||||
} |
||||
} |
@ -0,0 +1,90 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineClientProperties; |
||||
import com.fine.theme.utils.FineUIStyle; |
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatPanelUI; |
||||
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||
import com.fr.design.gui.ibutton.UICombinationButton; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.Color; |
||||
import java.awt.Graphics; |
||||
import java.beans.PropertyChangeEvent; |
||||
|
||||
import static com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; |
||||
|
||||
/** |
||||
* 双组件按钮UI |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/12/21 |
||||
*/ |
||||
public class FineCombinationButtonUI extends FlatPanelUI { |
||||
@Styleable(dot = true) |
||||
protected Color background; |
||||
|
||||
@Styleable(dot = true) |
||||
protected int arc; |
||||
|
||||
|
||||
/** |
||||
* @param shared |
||||
* @since 2 |
||||
*/ |
||||
protected FineCombinationButtonUI(boolean shared) { |
||||
super(shared); |
||||
} |
||||
|
||||
/** |
||||
* 创建UI |
||||
* |
||||
* @param c 组件 |
||||
* @return ComponentUI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineCombinationButtonUI(false); |
||||
} |
||||
|
||||
@Override |
||||
public void installUI(JComponent c) { |
||||
super.installUI(c); |
||||
background = FineUIUtils.getUIColor("CombinationButton.background", "desktop"); |
||||
arc = FineUIUtils.getUIInt("CombinationButton.arc", "Component.arc"); |
||||
} |
||||
|
||||
@Override |
||||
public void uninstallUI(JComponent c) { |
||||
super.uninstallUI(c); |
||||
} |
||||
|
||||
@Override |
||||
public void paint(Graphics g, JComponent c) { |
||||
paintBackground(g, c); |
||||
super.paint(g, c); |
||||
} |
||||
|
||||
protected void paintBackground(Graphics g, JComponent c) { |
||||
FlatUIUtils.setRenderingHints(g); |
||||
g.setColor(background); |
||||
g.fillRoundRect(0, 0, c.getWidth(), c.getHeight(), arc, arc); |
||||
} |
||||
|
||||
@Override |
||||
public void propertyChange(PropertyChangeEvent e) { |
||||
super.propertyChange(e); |
||||
switch (e.getPropertyName()) { |
||||
case FineClientProperties.STYLE_CLASS: |
||||
UICombinationButton b = (UICombinationButton) e.getSource(); |
||||
if (FineUIStyle.STYLE_PRIMARY.equals(e.getNewValue())) { |
||||
b.setPrimary(); |
||||
} |
||||
b.repaint(); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,41 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.formdev.flatlaf.ui.FlatComboBoxUI; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.JButton; |
||||
import javax.swing.SwingConstants; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.Graphics2D; |
||||
|
||||
/** |
||||
* 提供 {@link javax.swing.JComboBox} 的UI类 |
||||
* |
||||
* @author Levy.Xie |
||||
* @since 11.0 |
||||
* Created on 2023/12/07 |
||||
*/ |
||||
public class FineComboBoxUI extends FlatComboBoxUI { |
||||
|
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineComboBoxUI(); |
||||
} |
||||
|
||||
@Override |
||||
protected JButton createArrowButton() { |
||||
return new FineComboBoxButton(); |
||||
} |
||||
|
||||
protected class FineComboBoxButton extends FlatComboBoxButton { |
||||
|
||||
@Override |
||||
protected void paintArrow(Graphics2D g) { |
||||
if (isPopupVisible(comboBox)) { |
||||
setDirection(SwingConstants.NORTH); |
||||
} else { |
||||
setDirection(SwingConstants.SOUTH); |
||||
} |
||||
super.paintArrow(g); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,309 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatPanelUI; |
||||
import com.fr.design.style.background.gradient.GradientBar; |
||||
import com.fr.design.style.background.gradient.SelectColorPointBtn; |
||||
import com.fr.stable.AssistUtils; |
||||
import com.fr.stable.os.OperatingSystem; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.UIManager; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.Color; |
||||
import java.awt.Dimension; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.LinearGradientPaint; |
||||
import java.awt.RenderingHints; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
import java.awt.event.MouseListener; |
||||
import java.awt.event.MouseMotionAdapter; |
||||
import java.awt.geom.Path2D; |
||||
import java.awt.geom.Point2D; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 渐变色滑块 UI类 |
||||
* |
||||
* @author Leo.Qin |
||||
* @since 11.0 |
||||
* Created on 2023/12/19 |
||||
*/ |
||||
public class FineGradientBarUI extends FlatPanelUI { |
||||
|
||||
private int directionalShapeSize; |
||||
private int recHeight; |
||||
private int width; |
||||
private int height; |
||||
private int borderWidth; |
||||
private Color borderColor; |
||||
private Color thumbBorderColor; |
||||
private Color hoverThumbColor; |
||||
private Color pressedThumbColor; |
||||
|
||||
private MouseMotionAdapter mouseMotionListener; |
||||
|
||||
GradientBar gradientBar; |
||||
private List<SelectColorPointBtn> list; |
||||
private SelectColorPointBtn p1; |
||||
private SelectColorPointBtn p2; |
||||
private MouseListener mouseListener; |
||||
private double offset = 0.0001; |
||||
|
||||
boolean[] hoverStatus; |
||||
|
||||
protected FineGradientBarUI(boolean shared) { |
||||
super(shared); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 创建UI |
||||
* |
||||
* @param c 组件 |
||||
* @return UI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineGradientBarUI(false); |
||||
} |
||||
|
||||
@Override |
||||
public void installUI(JComponent c) { |
||||
super.installUI(c); |
||||
installDefaults(c); |
||||
|
||||
gradientBar = (GradientBar) c; |
||||
mouseMotionListener = new TrackMotionListener(); |
||||
mouseListener = new TrackMouseListener(); |
||||
gradientBar.addMouseMotionListener(mouseMotionListener); |
||||
gradientBar.addMouseListener(mouseListener); |
||||
} |
||||
|
||||
private void installDefaults(JComponent c) { |
||||
directionalShapeSize = FineUIUtils.getAndScaleInt("GradientBar.thumbWidth", 12); |
||||
recHeight = FineUIUtils.getAndScaleInt("GradientBar.recHeight", 30); |
||||
width = FineUIUtils.getAndScaleInt("GradientBar.recWidth", 160); |
||||
height = recHeight + directionalShapeSize; |
||||
borderWidth = FineUIUtils.getAndScaleInt("GradientBar.borderWidth", 1); |
||||
borderColor = UIManager.getColor("GradientBar.borderColor"); |
||||
thumbBorderColor = UIManager.getColor("GradientBar.thumbBorderColor"); |
||||
hoverThumbColor = UIManager.getColor("GradientBar.hoverThumbColor"); |
||||
pressedThumbColor = UIManager.getColor("GradientBar.pressedThumbColor"); |
||||
} |
||||
|
||||
private class TrackMouseListener extends MouseAdapter { |
||||
@Override |
||||
public void mouseExited(MouseEvent e) { |
||||
for (int i = 0; i < list.size(); i++) { |
||||
SelectColorPointBtn selectColorPointBtn = list.get(i); |
||||
selectColorPointBtn.setHover(false); |
||||
hoverStatus[i] = false; |
||||
} |
||||
|
||||
gradientBar.repaint(); |
||||
} |
||||
|
||||
@Override |
||||
public void mouseEntered(MouseEvent e) { |
||||
checkHoverStatus(e); |
||||
} |
||||
|
||||
@Override |
||||
public void mousePressed(MouseEvent e) { |
||||
for (SelectColorPointBtn btn : list) { |
||||
boolean hover = isOverBtn(e, btn); |
||||
btn.setPressed(hover); |
||||
} |
||||
|
||||
gradientBar.repaint(); |
||||
} |
||||
|
||||
@Override |
||||
public void mouseReleased(MouseEvent e) { |
||||
for (SelectColorPointBtn btn : list) { |
||||
btn.setPressed(false); |
||||
} |
||||
|
||||
gradientBar.repaint(); |
||||
} |
||||
} |
||||
|
||||
|
||||
private class TrackMotionListener extends MouseMotionAdapter { |
||||
int index; |
||||
|
||||
@Override |
||||
public void mouseDragged(MouseEvent e) { |
||||
if (!gradientBar.isDraggable()) { |
||||
return; |
||||
} |
||||
index = getSelectedIndex(e, index); |
||||
int halfSize = directionalShapeSize / 2; |
||||
boolean x = e.getX() <= gradientBar.getWidth() - halfSize && e.getX() >= halfSize; |
||||
if (x) { |
||||
list.get(index).setStartPosition((double) (e.getX() - halfSize) / (gradientBar.getWidth() - directionalShapeSize)); |
||||
gradientBar.repaint(); |
||||
} |
||||
} |
||||
|
||||
private int getSelectedIndex(MouseEvent e, int index) { |
||||
int oldIndex = index; |
||||
|
||||
for (int i = 0; i < list.size(); i++) { |
||||
if (list.get(i).contains(e.getX(), e.getY())) { |
||||
index = i; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (OperatingSystem.isLinux() && AssistUtils.equals(oldIndex, index)) { |
||||
if (Math.abs(p1.getX() - e.getX()) > Math.abs(p2.getX() - e.getX())) { |
||||
index = 1; |
||||
} else { |
||||
index = 0; |
||||
} |
||||
} |
||||
return index; |
||||
} |
||||
|
||||
@Override |
||||
public void mouseMoved(MouseEvent e) { |
||||
checkHoverStatus(e); |
||||
} |
||||
|
||||
} |
||||
|
||||
private void checkHoverStatus(MouseEvent e) { |
||||
boolean repaint = false; |
||||
for (int i = 0; i < list.size(); i++) { |
||||
SelectColorPointBtn btn = list.get(i); |
||||
boolean hover = isOverBtn(e, btn); |
||||
if (hoverStatus[i] != hover) { |
||||
repaint = true; |
||||
hoverStatus[i] = hover; |
||||
btn.setHover(hover); |
||||
} |
||||
} |
||||
|
||||
if (repaint) { |
||||
gradientBar.repaint(); |
||||
} |
||||
} |
||||
|
||||
private boolean isOverBtn(MouseEvent e, SelectColorPointBtn btn) { |
||||
return btn.contains(e.getX(), e.getY()); |
||||
} |
||||
|
||||
@Override |
||||
public void paint(Graphics g, JComponent c) { |
||||
Graphics2D g2 = (Graphics2D) g; |
||||
|
||||
|
||||
gradientBar = (GradientBar) c; |
||||
list = gradientBar.getList(); |
||||
p1 = gradientBar.getSelectColorPointBtnP1(); |
||||
p2 = gradientBar.getSelectColorPointBtnP2(); |
||||
if (hoverStatus == null) { |
||||
hoverStatus = new boolean[list.size()]; |
||||
} |
||||
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); |
||||
|
||||
GradientBar component = (GradientBar) c; |
||||
List<SelectColorPointBtn> btnList = component.getList(); |
||||
Collections.sort(btnList); |
||||
|
||||
|
||||
paintBorder(g2, component); |
||||
paintContent(g2, component); |
||||
paintButton(g2, btnList); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 实际绘制区域x范围(directionalShapeSize / 2, width - directionalShapeSize) |
||||
*/ |
||||
private void paintContent(Graphics2D g2d, GradientBar c) { |
||||
List<SelectColorPointBtn> btnList = c.getList(); |
||||
|
||||
int halfSize = directionalShapeSize / 2; |
||||
Point2D start = new Point2D.Float(halfSize, 0); |
||||
Point2D end = new Point2D.Float(c.getWidth() - halfSize, 0); |
||||
|
||||
|
||||
Collections.sort(btnList); |
||||
Color[] colors = new Color[btnList.size()]; |
||||
for (int i = 0; i < btnList.size(); i++) { |
||||
colors[i] = btnList.get(i).getColorInner(); |
||||
} |
||||
|
||||
float[] dist = getColorFloats(c, btnList); |
||||
LinearGradientPaint paint = new LinearGradientPaint(start, end, dist, colors); |
||||
g2d.setPaint(paint); |
||||
g2d.fillRect(halfSize + borderWidth, borderWidth, c.getWidth() - directionalShapeSize - borderWidth * 2, recHeight - borderWidth * 2); |
||||
} |
||||
|
||||
private float[] getColorFloats(GradientBar c, List<SelectColorPointBtn> btnList) { |
||||
float[] dist = new float[btnList.size()]; |
||||
for (int i = 0; i < btnList.size(); i++) { |
||||
if (btnList.get(i).getStartPosition() < 0) { |
||||
dist[i] = 0; |
||||
} else if (btnList.get(i).getStartPosition() > 1) { |
||||
dist[i] = 1; |
||||
} else { |
||||
dist[i] = (float) btnList.get(i).getStartPosition(); |
||||
} |
||||
|
||||
btnList.get(i).setX(dist[i] * (c.getWidth() - directionalShapeSize) + (double) directionalShapeSize / 2); |
||||
btnList.get(i).setY(recHeight); |
||||
} |
||||
|
||||
float dist1 = dist[btnList.size() - 1]; |
||||
float dist2 = dist[btnList.size() - 2]; |
||||
if (AssistUtils.equals(dist1, dist2)) { |
||||
dist[btnList.size() - 1] = (float) (dist2 + offset); |
||||
} |
||||
return dist; |
||||
} |
||||
|
||||
private void paintBorder(Graphics2D g2d, GradientBar c) { |
||||
int halfSize = directionalShapeSize / 2; |
||||
if (borderColor == null) { |
||||
return; |
||||
} |
||||
g2d.setColor(borderColor); |
||||
g2d.fillRect(halfSize, 0, c.getWidth() - directionalShapeSize, recHeight); |
||||
} |
||||
|
||||
private void paintButton(Graphics2D g2d, List<SelectColorPointBtn> list) { |
||||
|
||||
for (SelectColorPointBtn selectColorPointBtn : list) { |
||||
Path2D directionalThumbShape = FineSliderUI.createDirectionalThumbShape((float) selectColorPointBtn.getX() - (float) directionalShapeSize / 2, (float) selectColorPointBtn.getY(), directionalShapeSize, directionalShapeSize, 0); |
||||
if (selectColorPointBtn.isHover() && hoverThumbColor != null) { |
||||
g2d.setColor(hoverThumbColor); |
||||
g2d.fill(directionalThumbShape); |
||||
} else if (selectColorPointBtn.isPressed() && pressedThumbColor != null) { |
||||
g2d.setColor(pressedThumbColor); |
||||
g2d.fill(directionalThumbShape); |
||||
} else if (thumbBorderColor != null) { |
||||
g2d.setColor(thumbBorderColor); |
||||
g2d.fill(directionalThumbShape); |
||||
} |
||||
selectColorPointBtn.updatePath(directionalThumbShape); |
||||
|
||||
Path2D innerThumbShape = FineSliderUI.createDirectionalThumbShape((float) selectColorPointBtn.getX() - (float) directionalShapeSize / 2 + borderWidth, (float) selectColorPointBtn.getY() + borderWidth, directionalShapeSize - borderWidth * 2, directionalShapeSize - borderWidth * 2, 0); |
||||
g2d.setColor(selectColorPointBtn.getColorInner()); |
||||
g2d.fill(innerThumbShape); |
||||
} |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public Dimension getPreferredSize(JComponent c) { |
||||
return new Dimension(width, height); |
||||
} |
||||
} |
@ -0,0 +1,74 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import javax.swing.plaf.PanelUI; |
||||
import java.awt.Color; |
||||
import java.awt.Dimension; |
||||
import java.awt.Graphics; |
||||
|
||||
import static com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; |
||||
|
||||
/** |
||||
* HeadGroup 的UI类 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/12/15 |
||||
*/ |
||||
public class FineHeadGroupUI extends PanelUI { |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color background; |
||||
|
||||
@Styleable(dot = true) |
||||
protected int arc; |
||||
|
||||
/** |
||||
* 创建UI |
||||
* |
||||
* @param c 组件 |
||||
* @return ComponentUI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineHeadGroupUI(); |
||||
} |
||||
|
||||
@Override |
||||
public void installUI(JComponent c) { |
||||
super.installUI(c); |
||||
background = FineUIUtils.getUIColor("HeadGroup.background", "desktop"); |
||||
arc = FineUIUtils.getUIInt("HeadGroup.arc", "Component.arc"); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void uninstallUI(JComponent c) { |
||||
super.uninstallUI(c); |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getMinimumSize(JComponent component) { |
||||
return new Dimension(0, 0); |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getMaximumSize(JComponent component) { |
||||
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); |
||||
} |
||||
|
||||
@Override |
||||
public void update(Graphics g, JComponent c) { |
||||
paintBackground(g, c); |
||||
paint(g, c); |
||||
} |
||||
|
||||
protected void paintBackground(Graphics g, JComponent c) { |
||||
FlatUIUtils.setRenderingHints(g); |
||||
g.setColor(background); |
||||
g.fillRoundRect(0, 0, c.getWidth(), c.getHeight(), arc, arc); |
||||
} |
||||
} |
@ -0,0 +1,123 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatButtonUI; |
||||
import com.formdev.flatlaf.ui.FlatPanelUI; |
||||
|
||||
import javax.swing.AbstractButton; |
||||
import javax.swing.JComponent; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.UIManager; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.beans.PropertyChangeEvent; |
||||
|
||||
/** |
||||
* Input输入框类组件 UI类 |
||||
* |
||||
* @author Leo.Qin |
||||
* @since 11.0 |
||||
* Created on 2023/12/11 |
||||
*/ |
||||
public class FineInputUI extends FlatPanelUI { |
||||
|
||||
private static final String ENABLED = "enabled"; |
||||
private static final String EDITABLE = "editable"; |
||||
private final int defaultArc = 5; |
||||
|
||||
public FineInputUI(boolean shared) { |
||||
super(shared); |
||||
} |
||||
|
||||
/** |
||||
* 创建UI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineInputUI(false); |
||||
} |
||||
|
||||
@Override |
||||
public void installUI(JComponent c) { |
||||
super.installUI(c); |
||||
c.setBackground(UIManager.getColor("Input.background")); |
||||
c.setBorder(UIManager.getBorder("Input.border")); |
||||
} |
||||
|
||||
@Override |
||||
protected void installDefaults(JPanel p) { |
||||
super.installDefaults(p); |
||||
arc = FineUIUtils.getAndScaleInt("Input.arc", defaultArc); |
||||
} |
||||
|
||||
@Override |
||||
public void propertyChange(PropertyChangeEvent e) { |
||||
String propertyName = e.getPropertyName(); |
||||
if (EDITABLE.equals(propertyName) || ENABLED.equals(propertyName)) { |
||||
updateBackground(e); |
||||
} else { |
||||
super.propertyChange(e); |
||||
} |
||||
} |
||||
|
||||
private void updateBackground(PropertyChangeEvent e) { |
||||
JPanel source = (JPanel) e.getSource(); |
||||
if (e.getNewValue() == Boolean.FALSE) { |
||||
source.setBackground(UIManager.getColor("Input.disabledBackground")); |
||||
} else { |
||||
source.setBackground(UIManager.getColor("Input.background")); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* input输入框中的Button UI类 |
||||
* |
||||
* @author Leo.Qin |
||||
* @since 11.0 |
||||
* Created on 2023/12/12 |
||||
*/ |
||||
public static class FineInputButtonUI extends FlatButtonUI { |
||||
|
||||
public FineInputButtonUI(boolean shared) { |
||||
super(shared); |
||||
} |
||||
|
||||
/** |
||||
* 创建UI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineInputButtonUI(false); |
||||
} |
||||
|
||||
@Override |
||||
public void installUI(JComponent c) { |
||||
super.installUI(c); |
||||
c.setBorder(null); |
||||
c.setOpaque(false); |
||||
} |
||||
|
||||
@Override |
||||
protected void installDefaults(AbstractButton b) { |
||||
super.installDefaults(b); |
||||
hoverBackground = UIManager.getColor("InputButton.hoverBackground"); |
||||
pressedBackground = UIManager.getColor("InputButton.pressedBackground"); |
||||
background = UIManager.getColor("InputButton.background"); |
||||
} |
||||
|
||||
@Override |
||||
public void propertyChange(AbstractButton b, PropertyChangeEvent e) { |
||||
String propertyName = e.getPropertyName(); |
||||
if (EDITABLE.equals(propertyName) || ENABLED.equals(propertyName)) { |
||||
updateBackground(b, e); |
||||
} else { |
||||
super.propertyChange(b, e); |
||||
} |
||||
} |
||||
|
||||
private void updateBackground(AbstractButton b, PropertyChangeEvent e) { |
||||
if (e.getNewValue() == Boolean.FALSE) { |
||||
b.setBackground(UIManager.getColor("InputButton.disabledBackground")); |
||||
} else { |
||||
b.setBackground(UIManager.getColor("InputButton.background")); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,249 @@
|
||||
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), |
||||
new SvgIconSource("remove", "com/fine/theme/icon/remove.svg", true), |
||||
new SvgIconSource("search", "com/fine/theme/icon/search.svg", true), |
||||
new SvgIconSource("add", "com/fine/theme/icon/add.svg", true), |
||||
new SvgIconSource("drag_left", "com/fine/theme/icon/drag_left.svg", true), |
||||
new SvgIconSource("drag_right", "com/fine/theme/icon/drag_right.svg", true), |
||||
new SvgIconSource("down_arrow", "com/fine/theme/icon/down_arrow.svg", true), |
||||
new SvgIconSource("down_arrow_12", "com/fine/theme/icon/down_arrow.svg", true, 12), |
||||
new SvgIconSource("up_arrow_12", "com/fine/theme/icon/up_arrow.svg", true, 12), |
||||
new SvgIconSource("select", "com/fine/theme/icon/select.svg", true), |
||||
|
||||
// 数据集相关Icon
|
||||
new SvgIconSource("database", "com/fine/theme/icon/dataset/database.svg", true), |
||||
new SvgIconSource("preview", "com/fine/theme/icon/dataset/preview.svg", true), |
||||
new SvgIconSource("connection", "com/fine/theme/icon/dataset/connection.svg"), |
||||
new SvgIconSource("class_table_data", "com/fine/theme/icon/dataset/class_table_data.svg", true), |
||||
new SvgIconSource("data_table", "com/fine/theme/icon/dataset/data_table.svg", true), |
||||
new SvgIconSource("multi", "com/fine/theme/icon/dataset/multi.svg", true), |
||||
new SvgIconSource("file", "com/fine/theme/icon/dataset/file.svg", true), |
||||
new SvgIconSource("tree", "com/fine/theme/icon/dataset/tree.svg", true), |
||||
new SvgIconSource("store_procedure", "com/fine/theme/icon/dataset/store_procedure.svg", true), |
||||
new SvgIconSource("batch_esd_on", "com/fine/theme/icon/dataset/batch_esd_on.svg", true), |
||||
new SvgIconSource("batch_esd_off", "com/fine/theme/icon/dataset/batch_esd_off.svg", true), |
||||
new SvgIconSource("edit", "com/fine/theme/icon/dataset/edit.svg", true), |
||||
new SvgIconSource("server_database", "com/fine/theme/icon/dataset/server_database.svg", true), |
||||
new SvgIconSource("field", "com/fine/theme/icon/dataset/field.svg", true), |
||||
|
||||
// 目录树相关Icon
|
||||
new SvgIconSource("folder", "com/fine/theme/icon/filetree/folder.svg", true), |
||||
new SvgIconSource("folder_open", "com/fine/theme/icon/filetree/folder_open.svg", true), |
||||
new SvgIconSource("cpt_icon", "com/fine/theme/icon/filetree/cpt_icon.svg", true), |
||||
new SvgIconSource("frm_icon", "com/fine/theme/icon/filetree/frm_icon.svg", true), |
||||
new SvgIconSource("fvs_icon", "com/fine/theme/icon/filetree/fvs_icon.svg", true), |
||||
new SvgIconSource("excel_icon", "com/fine/theme/icon/filetree/excel_icon.svg", true), |
||||
new SvgIconSource("minus", "com/fine/theme/icon/filetree/minus.svg", true), |
||||
new SvgIconSource("plus", "com/fine/theme/icon/filetree/plus.svg", true), |
||||
new SvgIconSource("locate", "com/fine/theme/icon/filetree/locate.svg", true), |
||||
new SvgIconSource("rename", "com/fine/theme/icon/filetree/rename.svg", true), |
||||
new SvgIconSource("collapse_all", "com/fine/theme/icon/filetree/collapse_all.svg", true), |
||||
new SvgIconSource("vcs_list", "com/fine/theme/icon/filetree/vcs_list.svg", true), |
||||
new SvgIconSource("view_folder", "com/fine/theme/icon/filetree/view_folder.svg", true), |
||||
new SvgIconSource("refresh", "com/fine/theme/icon/filetree/refresh.svg", true), |
||||
new SvgIconSource("new_folder", "com/fine/theme/icon/filetree/new_folder.svg", true), |
||||
|
||||
// 文件类型
|
||||
new SvgIconSource("add_report", "com/fine/theme/icon/filetree/filetype/add_report.svg", true), |
||||
new SvgIconSource("add_word", "com/fine/theme/icon/filetree/filetype/add_word.svg", true), |
||||
new SvgIconSource("bmpFile", "com/fine/theme/icon/filetree/filetype/bmpFile.svg", true), |
||||
new SvgIconSource("chtFile", "com/fine/theme/icon/filetree/filetype/chtFile.svg", true), |
||||
new SvgIconSource("classFile", "com/fine/theme/icon/filetree/filetype/classFile.svg", true), |
||||
new SvgIconSource("cpt_locked", "com/fine/theme/icon/filetree/filetype/cpt_locked.svg", true), |
||||
new SvgIconSource("excel_import", "com/fine/theme/icon/filetree/filetype/excel_import.svg", true), |
||||
new SvgIconSource("excelFile", "com/fine/theme/icon/filetree/filetype/excelFile.svg", true), |
||||
new SvgIconSource("flashFile", "com/fine/theme/icon/filetree/filetype/flashFile.svg", true), |
||||
new SvgIconSource("frm_locked", "com/fine/theme/icon/filetree/filetype/frm_locked.svg", true), |
||||
new SvgIconSource("gifFile", "com/fine/theme/icon/filetree/filetype/gifFile.svg", true), |
||||
new SvgIconSource("htmlFile", "com/fine/theme/icon/filetree/filetype/htmlFile.svg", true), |
||||
new SvgIconSource("jarFile", "com/fine/theme/icon/filetree/filetype/jarFile.svg", true), |
||||
new SvgIconSource("javaFile", "com/fine/theme/icon/filetree/filetype/javaFile.svg", true), |
||||
new SvgIconSource("jpgFile", "com/fine/theme/icon/filetree/filetype/jpgFile.svg", true), |
||||
new SvgIconSource("jsFile", "com/fine/theme/icon/filetree/filetype/jsFile.svg", true), |
||||
new SvgIconSource("jspFile", "com/fine/theme/icon/filetree/filetype/jspFile.svg", true), |
||||
new SvgIconSource("pdfFile", "com/fine/theme/icon/filetree/filetype/pdfFile.svg", true), |
||||
new SvgIconSource("pngFile", "com/fine/theme/icon/filetree/filetype/pngFile.svg", true), |
||||
new SvgIconSource("sqlFile", "com/fine/theme/icon/filetree/filetype/sqlFile.svg", true), |
||||
new SvgIconSource("wordFile", "com/fine/theme/icon/filetree/filetype/wordFile.svg", true), |
||||
new SvgIconSource("xlsFile", "com/fine/theme/icon/filetree/filetype/xlsFile.svg", true), |
||||
new SvgIconSource("xmlFile", "com/fine/theme/icon/filetree/filetype/xmlFile.svg", true), |
||||
|
||||
// 属性面板Icon
|
||||
new SvgIconSource("cellattr", "com/fine/theme/icon/propertiestab/cellattr.svg", false, 18), |
||||
new SvgIconSource("cellattr_disabled", "com/fine/theme/icon/propertiestab/cellattr_disabled.svg", false, 18), |
||||
new SvgIconSource("cellattr_selected", "com/fine/theme/icon/propertiestab/cellattr_selected.svg", false, 18), |
||||
new SvgIconSource("cellelement", "com/fine/theme/icon/propertiestab/cellelement.svg", false, 18), |
||||
new SvgIconSource("cellelement_disabled", "com/fine/theme/icon/propertiestab/cellelement_disabled.svg", false, 18), |
||||
new SvgIconSource("cellelement_selected", "com/fine/theme/icon/propertiestab/cellelement_selected.svg", false, 18), |
||||
new SvgIconSource("conditionattr", "com/fine/theme/icon/propertiestab/conditionattr.svg", false, 18), |
||||
new SvgIconSource("conditionattr_disabled", "com/fine/theme/icon/propertiestab/conditionattr_disabled.svg", false, 18), |
||||
new SvgIconSource("conditionattr_selected", "com/fine/theme/icon/propertiestab/conditionattr_selected.svg", false, 18), |
||||
new SvgIconSource("floatelement", "com/fine/theme/icon/propertiestab/floatelement.svg", false, 18), |
||||
new SvgIconSource("floatelement_disabled", "com/fine/theme/icon/propertiestab/floatelement_disabled.svg", false, 18), |
||||
new SvgIconSource("floatelement_selected", "com/fine/theme/icon/propertiestab/floatelement_selected.svg", false, 18), |
||||
new SvgIconSource("hyperlink", "com/fine/theme/icon/propertiestab/hyperlink.svg", false, 18), |
||||
new SvgIconSource("hyperlink_disabled", "com/fine/theme/icon/propertiestab/hyperlink_disabled.svg", false, 18), |
||||
new SvgIconSource("hyperlink_selected", "com/fine/theme/icon/propertiestab/hyperlink_selected.svg", false, 18), |
||||
new SvgIconSource("widgetlib", "com/fine/theme/icon/propertiestab/widgetlib.svg", false, 18), |
||||
new SvgIconSource("widgetlib_disabled", "com/fine/theme/icon/propertiestab/widgetlib_disabled.svg", false, 18), |
||||
new SvgIconSource("widgetlib_selected", "com/fine/theme/icon/propertiestab/widgetlib_selected.svg", false, 18), |
||||
new SvgIconSource("widgetsettings", "com/fine/theme/icon/propertiestab/widgetsettings.svg", false, 18), |
||||
new SvgIconSource("widgetsettings_disabled", "com/fine/theme/icon/propertiestab/widgetsettings_disabled.svg", false, 18), |
||||
new SvgIconSource("widgetsettings_selected", "com/fine/theme/icon/propertiestab/widgetsettings_selected.svg", false, 18), |
||||
new SvgIconSource("configuredroles", "com/fine/theme/icon/propertiestab/configuredroles.svg", false, 18), |
||||
new SvgIconSource("configuredroles_selected", "com/fine/theme/icon/propertiestab/configuredroles_selected.svg", false, 18), |
||||
new SvgIconSource("configuredroles_disabled", "com/fine/theme/icon/propertiestab/configuredroles_disabled.svg", false, 18), |
||||
new SvgIconSource("authorityedit", "com/fine/theme/icon/propertiestab/authorityedit.svg", false, 18), |
||||
new SvgIconSource("authorityedit_disabled", "com/fine/theme/icon/propertiestab/authorityedit_disabled.svg", false, 18), |
||||
new SvgIconSource("authorityedit_selected", "com/fine/theme/icon/propertiestab/authorityedit_selected.svg", false, 18), |
||||
|
||||
|
||||
// sheet标签栏相关icon
|
||||
new SvgIconSource("add_worksheet", "com/fine/theme/icon/sheet/add_sheet.svg", true), |
||||
// TODO: 待视觉提供后替换
|
||||
new SvgIconSource("add_polysheet", "com/fine/theme/icon/sheet/add_frm.svg", true), |
||||
|
||||
// CheckBox相关Icon
|
||||
new SvgIconSource("checkbox_checked", "com/fine/theme/icon/checkbox/checked.svg", true), |
||||
new SvgIconSource("checkbox_unchecked", "com/fine/theme/icon/checkbox/unchecked.svg", true), |
||||
new SvgIconSource("checkbox_part_checked", "com/fine/theme/icon/checkbox/part_checked.svg", true), |
||||
new SvgIconSource("checkbox_hovered", "com/fine/theme/icon/checkbox/hovered.svg", true), |
||||
|
||||
// radioButton相关icon
|
||||
new SvgIconSource("radio_selected", "com/fine/theme/icon/radio/radio_selected.svg", true), |
||||
new SvgIconSource("radio_unselected", "com/fine/theme/icon/radio/radio_unselected.svg", true), |
||||
|
||||
// 菜单栏Icon
|
||||
new SvgIconSource("bold", "com/fine/theme/icon/font/bold.svg"), |
||||
new SvgIconSource("italic", "com/fine/theme/icon/font/italic.svg"), |
||||
new SvgIconSource("underline", "com/fine/theme/icon/font/underline.svg"), |
||||
new SvgIconSource("foreground", "com/fine/theme/icon/font/foreground.svg"), |
||||
new SvgIconSource("background", "com/fine/theme/icon/font/background.svg"), |
||||
new SvgIconSource("h_left", "com/fine/theme/icon/cellstyle/h_left.svg"), |
||||
new SvgIconSource("h_center", "com/fine/theme/icon/cellstyle/h_center.svg"), |
||||
new SvgIconSource("h_right", "com/fine/theme/icon/cellstyle/h_right.svg"), |
||||
new SvgIconSource("noboder", "com/fine/theme/icon/noboder.svg", true), |
||||
new SvgIconSource("merge", "com/fine/theme/icon/merge/merge.svg", true), |
||||
new SvgIconSource("unmerge", "com/fine/theme/icon/merge/unmerge.svg", true), |
||||
new SvgIconSource("bind_column", "com/fine/theme/icon/bindcolumn/bind_column.svg", true), |
||||
new SvgIconSource("text", "com/fine/theme/icon/insert/text.svg", true), |
||||
new SvgIconSource("richtext", "com/fine/theme/icon/insert/richtext.svg", true), |
||||
new SvgIconSource("formula", "com/fine/theme/icon/insert/formula.svg", true), |
||||
new SvgIconSource("chart", "com/fine/theme/icon/insert/chart.svg", true), |
||||
new SvgIconSource("image", "com/fine/theme/icon/insert/image.svg", true), |
||||
new SvgIconSource("bias", "com/fine/theme/icon/insert/bias.svg", true), |
||||
new SvgIconSource("sub_report", "com/fine/theme/icon/insert/sub_report.svg", true), |
||||
new SvgIconSource("chart_line", "com/fine/theme/icon/chart/chart_line.svg", true), |
||||
new SvgIconSource("popup", "com/fine/theme/icon/popup/popup.svg", true), |
||||
new SvgIconSource("clear", "com/fine/theme/icon/clear.svg", true), |
||||
new SvgIconSource("clear_hover", "com/fine/theme/icon/clear_hover.svg", true), |
||||
|
||||
// 工具栏
|
||||
new SvgIconSource("tool_copy", "com/fine/theme/icon/toolbar/copy.svg", true), |
||||
new SvgIconSource("move_down", "com/fine/theme/icon/toolbar/move_down.svg", true), |
||||
new SvgIconSource("move_up", "com/fine/theme/icon/toolbar/move_up.svg", true), |
||||
new SvgIconSource("move_left", "com/fine/theme/icon/toolbar/move_left.svg", true), |
||||
new SvgIconSource("move_right", "com/fine/theme/icon/toolbar/move_right.svg", true), |
||||
new SvgIconSource("tool_edit", "com/fine/theme/icon/toolbar/edit.svg", true), |
||||
new SvgIconSource("tool_edit_white", "com/fine/theme/icon/toolbar/edit_white.svg", true), |
||||
new SvgIconSource("tool_more", "com/fine/theme/icon/toolbar/more.svg", true), |
||||
new SvgIconSource("tool_more_hover", "com/fine/theme/icon/toolbar/more_hover.svg"), |
||||
new SvgIconSource("tool_config", "com/fine/theme/icon/toolbar/config.svg", true), |
||||
new SvgIconSource("add_popup", "com/fine/theme/icon/toolbar/add_popup.svg", true, 24), |
||||
|
||||
// 参数面板
|
||||
new SvgIconSource("param_edit", "com/fine/theme/icon/param/edit.svg", true, 24), |
||||
new SvgIconSource("param_edit_pressed", "com/fine/theme/icon/param/edit_pressed.svg", true, 24), |
||||
new SvgIconSource("param_hide", "com/fine/theme/icon/param/hide.svg", true, 24), |
||||
new SvgIconSource("param_hide_pressed", "com/fine/theme/icon/param/hide_pressed.svg", true, 24), |
||||
new SvgIconSource("param_view", "com/fine/theme/icon/param/view.svg", true, 18), |
||||
new SvgIconSource("param", "com/fine/theme/icon/param/param.svg", true), |
||||
|
||||
// 北区菜单栏
|
||||
new SvgIconSource("notification", "com/fine/theme/icon/notification/notification.svg"), |
||||
new SvgIconSource("notification_dot", "com/fine/theme/icon/notification/notification_dot.svg"), |
||||
|
||||
//东区面板
|
||||
new SvgIconSource("cellelement_small", "com/fine/theme/icon/cellelement.svg"), |
||||
|
||||
// 三角
|
||||
new SvgIconSource("triangle_down", "com/fine/theme/icon/triangle/triangle_down.svg"), |
||||
new SvgIconSource("triangle_down_small", "com/fine/theme/icon/triangle/triangle_down_small.svg"), |
||||
new SvgIconSource("triangle_left", "com/fine/theme/icon/triangle/triangle_left.svg"), |
||||
new SvgIconSource("triangle_left_small", "com/fine/theme/icon/triangle/triangle_left_small.svg"), |
||||
new SvgIconSource("triangle_right", "com/fine/theme/icon/triangle/triangle_right.svg"), |
||||
new SvgIconSource("triangle_right_small", "com/fine/theme/icon/triangle/triangle_right_small.svg"), |
||||
|
||||
// 滚动条
|
||||
new SvgIconSource("zoomIn", "com/fine/theme/icon/zoom/zoomIn.svg", true), |
||||
new SvgIconSource("zoomOut", "com/fine/theme/icon/zoom/zoomOut.svg", true), |
||||
|
||||
//排序
|
||||
new SvgIconSource("sort_asc", "com/fine/theme/icon/sort/sort_asc.svg", true), |
||||
new SvgIconSource("sort_desc", "com/fine/theme/icon/sort/sort_desc.svg", true), |
||||
new SvgIconSource("nosort", "com/fine/theme/icon/sort/nosort.svg", true), |
||||
|
||||
// 关闭
|
||||
new SvgIconSource("close", "com/fine/theme/icon/close/close.svg", true), |
||||
new SvgIconSource("close_round", "com/fine/theme/icon/close/close_round.svg", true), |
||||
|
||||
// 文字样式
|
||||
new SvgIconSource("add_parenthesis", "com/fine/theme/icon/font/add_parenthesis.svg", true), |
||||
new SvgIconSource("remove_parenthesis", "com/fine/theme/icon/font/remove_parenthesis.svg", true), |
||||
new SvgIconSource("shadow", "com/fine/theme/icon/font/shadow.svg", true), |
||||
new SvgIconSource("strike", "com/fine/theme/icon/font/strike.svg", true), |
||||
new SvgIconSource("sub", "com/fine/theme/icon/font/sub.svg", true), |
||||
new SvgIconSource("super", "com/fine/theme/icon/font/super.svg", true), |
||||
|
||||
new SvgIconSource("dot", "com/fine/theme/icon/dot.svg", true), |
||||
new SvgIconSource("expand_popup", "com/fine/theme/icon/popup/expand_popup.svg"), |
||||
new SvgIconSource("collapse_popup", "com/fine/theme/icon/popup/collapse_popup.svg"), |
||||
|
||||
new SvgIconSource("logMsg", "com/fine/theme/icon/log/logMsg.svg", true), |
||||
new SvgIconSource("logMsg_dot", "com/fine/theme/icon/log/logMsg_dot.svg", true), |
||||
|
||||
// 右键弹窗
|
||||
new SvgIconSource("cellClear", "com/fine/theme/icon/cell/cellClear.svg", true), |
||||
new SvgIconSource("cellExpandAttr", "com/fine/theme/icon/cell/cellExpandAttr.svg", true), |
||||
new SvgIconSource("cellStyleAttr", "com/fine/theme/icon/cell/cellStyleAttr.svg", true), |
||||
new SvgIconSource("cellOtherAttr", "com/fine/theme/icon/cell/cellOtherAttr.svg", true), |
||||
new SvgIconSource("cellWidgetAttr", "com/fine/theme/icon/cell/cellWidgetAttr.svg", true), |
||||
new SvgIconSource("cellConditionalAttr", "com/fine/theme/icon/cell/cellConditionalAttr.svg", true), |
||||
new SvgIconSource("cellHyperLinkAttr", "com/fine/theme/icon/cell/cellHyperLinkAttr.svg", true), |
||||
new SvgIconSource("cellPresentAttr", "com/fine/theme/icon/cell/cellPresentAttr.svg", true), |
||||
new SvgIconSource("cellElementAttr", "com/fine/theme/icon/cell/cellElementAttr.svg", true), |
||||
new SvgIconSource("move", "com/fine/theme/icon/filetree/move.svg", true), |
||||
new SvgIconSource("monochrome_copy", "com/fine/theme/icon/filetree/monochrome_copy.svg", true), |
||||
new SvgIconSource("monochrome_paste", "com/fine/theme/icon/filetree/monochrome_paste.svg", true) |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,41 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineClientProperties; |
||||
import com.formdev.flatlaf.ui.FlatMenuItemUI; |
||||
import com.fr.design.editlock.EditLockUtils; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.Graphics; |
||||
|
||||
/** |
||||
* menuItem UI类 |
||||
* |
||||
* @author Leo.Qin |
||||
* @since 11.0 |
||||
* Created on 2024/1/8 |
||||
*/ |
||||
public class FineMenuItemUI extends FlatMenuItemUI { |
||||
int iconSize = 16; |
||||
int rightMargin = 10; |
||||
|
||||
/** |
||||
* 创建UI |
||||
* |
||||
* @param c |
||||
* @return |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineMenuItemUI(); |
||||
} |
||||
|
||||
@Override |
||||
public void paint(Graphics g, JComponent c) { |
||||
super.paint(g, c); |
||||
|
||||
Object itemType = c.getClientProperty(FineClientProperties.MENU_ITEM_TYPE); |
||||
if (FineClientProperties.MENU_ITEM_TYPE_LOCK.equals(itemType)) { |
||||
g.drawImage(EditLockUtils.LOCKED_IMAGE, c.getWidth() - rightMargin - iconSize, (c.getHeight() - iconSize) / 2, iconSize, iconSize, null); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,40 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.icon.LazyIcon; |
||||
import com.formdev.flatlaf.ui.FlatMenuUI; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.Graphics; |
||||
|
||||
/** |
||||
* 弹窗菜单UI |
||||
* |
||||
* @author Leo.Qin |
||||
* @since 11.0 |
||||
* Created on 2024/1/8 |
||||
*/ |
||||
public class FineMenuUI extends FlatMenuUI { |
||||
|
||||
|
||||
/** |
||||
* 创建UI |
||||
* |
||||
* @param c |
||||
* @return |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineMenuUI(); |
||||
} |
||||
|
||||
@Override |
||||
protected void installDefaults() { |
||||
arrowIcon = new LazyIcon("triangle_right"); |
||||
super.installDefaults(); |
||||
} |
||||
|
||||
@Override |
||||
public void paint(Graphics g, JComponent c) { |
||||
super.paint(g, c); |
||||
} |
||||
} |
@ -0,0 +1,19 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatPopupMenuBorder; |
||||
|
||||
/** |
||||
* PopupMenu Border类 |
||||
* |
||||
* @author Leo.Qin |
||||
* @since 11.0 |
||||
* Created on 2023/12/25 |
||||
*/ |
||||
public class FinePopupMenuBorder extends FlatPopupMenuBorder { |
||||
|
||||
@Override |
||||
public int getArc() { |
||||
return FineUIUtils.getAndScaleInt("PopupMenu.arc", 5); |
||||
} |
||||
} |
@ -0,0 +1,70 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatPopupMenuSeparatorUI; |
||||
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.JSeparator; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.Insets; |
||||
import java.awt.geom.Rectangle2D; |
||||
|
||||
import static com.formdev.flatlaf.util.UIScale.scale; |
||||
|
||||
/** |
||||
* popup弹窗分割线UI |
||||
* |
||||
* @author Leo.Qin |
||||
* @since 11.0 |
||||
* Created on 2024/1/8 |
||||
*/ |
||||
public class FinePopupMenuSeparatorUI extends FlatPopupMenuSeparatorUI { |
||||
protected Insets insets; |
||||
|
||||
/** |
||||
* @param shared |
||||
* @since 2 |
||||
*/ |
||||
protected FinePopupMenuSeparatorUI(boolean shared) { |
||||
super(shared); |
||||
} |
||||
|
||||
/** |
||||
* 创建UI类 |
||||
* |
||||
* @param c |
||||
* @return |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FinePopupMenuSeparatorUI(false); |
||||
} |
||||
|
||||
@Override |
||||
protected void installDefaults(JSeparator s) { |
||||
super.installDefaults(s); |
||||
insets = FineUIUtils.getAndScaleUIInsets("PopupMenuSeparator.Insets", new Insets(0, 10, 0, 10)); |
||||
} |
||||
|
||||
@Override |
||||
public void paint(Graphics g, JComponent c) { |
||||
Graphics2D g2 = (Graphics2D) g.create(); |
||||
try { |
||||
FlatUIUtils.setRenderingHints(g2); |
||||
g2.setColor(c.getForeground()); |
||||
|
||||
float width = scale((float) stripeWidth); |
||||
float indent = scale((float) stripeIndent); |
||||
|
||||
if (((JSeparator) c).getOrientation() == JSeparator.VERTICAL) { |
||||
g2.fill(new Rectangle2D.Float(indent, insets.left, width - (insets.left + insets.right), c.getHeight())); |
||||
} else { |
||||
g2.fill(new Rectangle2D.Float(insets.left, indent, c.getWidth() - (insets.left + insets.right), width)); |
||||
} |
||||
} finally { |
||||
g2.dispose(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,58 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatPopupMenuUI; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.RenderingHints; |
||||
import java.awt.geom.RoundRectangle2D; |
||||
|
||||
/** |
||||
* PopupMenu UI类 |
||||
* |
||||
* @author Leo.Qin |
||||
* @since 11.0 |
||||
* Created on 2023/12/25 |
||||
*/ |
||||
public class FinePopupMenuUI extends FlatPopupMenuUI { |
||||
private int arc; |
||||
private final int DEFAULT_ARC = 10; |
||||
|
||||
/** |
||||
* 创建UI |
||||
* |
||||
* @param c 组件 |
||||
* @return UI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FinePopupMenuUI(); |
||||
} |
||||
|
||||
@Override |
||||
public void installDefaults() { |
||||
super.installDefaults(); |
||||
arc = FineUIUtils.getAndScaleInt("PopupMenu.arc", DEFAULT_ARC); |
||||
} |
||||
|
||||
@Override |
||||
public void paint(Graphics g, JComponent c) { |
||||
|
||||
// 绘制圆角矩形作为弹窗背景
|
||||
Graphics2D g2d = (Graphics2D) g; |
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||
RoundRectangle2D roundRect = new RoundRectangle2D.Double(0, 0, c.getWidth(), c.getHeight(), arc, arc); |
||||
g2d.setColor(c.getBackground()); |
||||
g2d.fill(roundRect); |
||||
|
||||
// 绘制组件内容
|
||||
super.paint(g, c); |
||||
} |
||||
|
||||
@Override |
||||
public void update(Graphics g, JComponent c) { |
||||
paint(g, c); |
||||
} |
||||
} |
@ -0,0 +1,28 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatRoundBorder; |
||||
|
||||
import javax.swing.UIManager; |
||||
import java.awt.Color; |
||||
import java.awt.Component; |
||||
|
||||
/** |
||||
* 报表编辑区域边框 |
||||
* |
||||
* @author Leo.Qin |
||||
* @since 11.0 |
||||
* Created on 2024/1/2 |
||||
*/ |
||||
public class FineReportComponentBorder extends FlatRoundBorder { |
||||
|
||||
@Override |
||||
protected int getArc(Component c) { |
||||
return FineUIUtils.getAndScaleInt("Center.arc", 10); |
||||
} |
||||
|
||||
@Override |
||||
protected Color getBorderColor(Component c) { |
||||
return UIManager.getColor("Center.ZoneBorderColor"); |
||||
} |
||||
} |
@ -0,0 +1,40 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatPanelUI; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.plaf.ComponentUI; |
||||
|
||||
/** |
||||
* 报表编辑区域UI |
||||
* |
||||
* @author Leo.Qin |
||||
* @since 11.0 |
||||
* Created on 2024/1/2 |
||||
*/ |
||||
public class FineReportComponentCompositeUI extends FlatPanelUI { |
||||
/** |
||||
* @param shared |
||||
* @since 2 |
||||
*/ |
||||
protected FineReportComponentCompositeUI(boolean shared) { |
||||
super(shared); |
||||
} |
||||
|
||||
/** |
||||
* 创建UI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineReportComponentCompositeUI(false); |
||||
} |
||||
|
||||
@Override |
||||
protected void installDefaults(JPanel p) { |
||||
super.installDefaults(p); |
||||
this.arc = FineUIUtils.getAndScaleInt("Center.arc", 10); |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,63 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatRoundBorder; |
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; |
||||
import com.fr.design.event.HoverAware; |
||||
|
||||
import java.awt.Color; |
||||
import java.awt.Component; |
||||
import java.awt.Paint; |
||||
import java.util.StringJoiner; |
||||
|
||||
|
||||
/** |
||||
* 通用的Border类,具备hover、click、禁用等多种状态 |
||||
* |
||||
* @author Levy.Xie |
||||
* @since 11.0 |
||||
* Created on 2023/12/06 |
||||
*/ |
||||
public class FineRoundBorder extends FlatRoundBorder { |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color borderColor = FineUIUtils.getUIColor("defaultBorderColor", "Component.borderColor"); |
||||
@Styleable(dot = true) |
||||
protected Color disabledBorderColor = FineUIUtils.getUIColor("defaultBorderColor", "Component.disabledBorderColor"); |
||||
@Styleable(dot = true) |
||||
protected Color highlightBorderColor = FineUIUtils.getUIColor("defaultHighlightBorderColor", "Component.focusedBorderColor"); |
||||
@Styleable(dot = true) |
||||
protected Color focusColor = FineUIUtils.getUIColor("defaultBorderFocusShadow", "Component.focusedBorderColor"); |
||||
|
||||
@Override |
||||
protected Paint getBorderColor(Component c) { |
||||
if (isEnabled(c)) { |
||||
if (c instanceof HoverAware && ((HoverAware) c).isHovered()) { |
||||
return getHoverBorderColor(); |
||||
} else { |
||||
return isFocused(c) ? focusedBorderColor : borderColor; |
||||
} |
||||
} |
||||
return disabledBorderColor; |
||||
} |
||||
|
||||
@Override |
||||
protected Color getFocusColor(Component c) { |
||||
return focusColor; |
||||
} |
||||
|
||||
protected Color getHoverBorderColor() { |
||||
return highlightBorderColor; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public String toString() { |
||||
return new StringJoiner(", ", FineRoundBorder.class.getSimpleName() + "[", "]") |
||||
.add("borderColor=" + borderColor) |
||||
.add("arc=" + arc) |
||||
.add("roundRect=" + roundRect) |
||||
.add("borderWidth=" + borderWidth) |
||||
.toString(); |
||||
} |
||||
} |
@ -0,0 +1,49 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineUIUtils; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import javax.swing.plaf.PanelUI; |
||||
import java.awt.Dimension; |
||||
|
||||
/** |
||||
* 选择框面板UI,应用于 {@link com.fr.design.style.AbstractSelectBox} |
||||
* |
||||
* @author Levy.Xie |
||||
* @since 11.0 |
||||
* Created on 2023/12/15 |
||||
*/ |
||||
public class FineSelectBoxUI extends PanelUI { |
||||
|
||||
private static final int DEFAULT_BOX_HEIGHT = 24; |
||||
|
||||
protected int boxHeight; |
||||
|
||||
/** |
||||
* 创建UI |
||||
* |
||||
* @param c 组件 |
||||
* @return ComponentUI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineSelectBoxUI(); |
||||
} |
||||
|
||||
@Override |
||||
public void installUI(JComponent c) { |
||||
super.installUI(c); |
||||
boxHeight = FineUIUtils.getAndScaleInt("ComboBox.comboHeight", DEFAULT_BOX_HEIGHT); |
||||
c.setBorder(new FineRoundBorder()); |
||||
} |
||||
|
||||
@Override |
||||
public void uninstallUI(JComponent c) { |
||||
super.uninstallUI(c); |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getPreferredSize(JComponent c) { |
||||
return new Dimension(c.getWidth(), boxHeight); |
||||
} |
||||
} |
@ -0,0 +1,344 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatSliderUI; |
||||
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||
import com.formdev.flatlaf.util.HiDPIUtils; |
||||
import com.formdev.flatlaf.util.UIScale; |
||||
import com.fr.stable.AssistUtils; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.JSlider; |
||||
import javax.swing.UIManager; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.Color; |
||||
import java.awt.Component; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.Rectangle; |
||||
import java.awt.Shape; |
||||
import java.awt.geom.Ellipse2D; |
||||
import java.awt.geom.Path2D; |
||||
import java.awt.geom.RoundRectangle2D; |
||||
import java.util.Dictionary; |
||||
import java.util.Enumeration; |
||||
|
||||
/** |
||||
* 滑块slider UI类 |
||||
* |
||||
* @author Leo.Qin |
||||
* @since 11.0 |
||||
* Created on 2023/12/15 |
||||
*/ |
||||
public class FineSliderUI extends FlatSliderUI { |
||||
|
||||
private final int DEFAULT_LABEL_HEIGHT = 13; |
||||
private Color defaultForeground; |
||||
private int defaultLabelHeight; |
||||
|
||||
/** |
||||
* 创建UI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineSliderUI(); |
||||
} |
||||
|
||||
@Override |
||||
protected void installDefaults(JSlider slider) { |
||||
super.installDefaults(slider); |
||||
defaultForeground = UIManager.getColor("Slider.foreground"); |
||||
defaultLabelHeight = FineUIUtils.getAndScaleInt("Slider.labelHeight", DEFAULT_LABEL_HEIGHT); |
||||
} |
||||
|
||||
@Override |
||||
protected void calculateLabelRect() { |
||||
|
||||
if (slider.getPaintLabels()) { |
||||
calLabelRectWhenPaint(); |
||||
} else { |
||||
calLabelRectWhenNotPaint(); |
||||
} |
||||
} |
||||
|
||||
private void calLabelRectWhenPaint() { |
||||
labelRect.y = 0; |
||||
|
||||
if (slider.getOrientation() == JSlider.HORIZONTAL) { |
||||
labelRect.x = tickRect.x - trackBuffer; |
||||
labelRect.width = tickRect.width + (trackBuffer * 2); |
||||
labelRect.height = getHeightOfTallestLabel(); |
||||
} else { |
||||
if (isLeftToRight(slider)) { |
||||
labelRect.x = tickRect.x + tickRect.width; |
||||
labelRect.width = getWidthOfWidestLabel(); |
||||
} else { |
||||
labelRect.width = getWidthOfWidestLabel(); |
||||
labelRect.x = tickRect.x - labelRect.width; |
||||
} |
||||
labelRect.height = tickRect.height + (trackBuffer * 2); |
||||
} |
||||
} |
||||
|
||||
private void calLabelRectWhenNotPaint() { |
||||
labelRect.y = 0; |
||||
|
||||
if (slider.getOrientation() == JSlider.HORIZONTAL) { |
||||
labelRect.x = tickRect.x; |
||||
labelRect.width = tickRect.width; |
||||
labelRect.height = 0; |
||||
} else { |
||||
if (isLeftToRight(slider)) { |
||||
labelRect.x = tickRect.x + tickRect.width; |
||||
} else { |
||||
labelRect.x = tickRect.x; |
||||
} |
||||
labelRect.width = 0; |
||||
labelRect.height = tickRect.height; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void calculateTrackRect() { |
||||
if (slider.getOrientation() == JSlider.HORIZONTAL) { |
||||
calHorizontalTrackRect(); |
||||
} else { |
||||
calVerticalTrackRect(); |
||||
} |
||||
} |
||||
|
||||
private void calVerticalTrackRect() { |
||||
int centerSpacing; |
||||
centerSpacing = thumbRect.width; |
||||
if (isLeftToRight(slider)) { |
||||
if (slider.getPaintTicks()) { |
||||
centerSpacing += getTickLength(); |
||||
} |
||||
if (slider.getPaintLabels()) { |
||||
centerSpacing += getWidthOfWidestLabel(); |
||||
} |
||||
} else { |
||||
if (slider.getPaintTicks()) { |
||||
centerSpacing -= getTickLength(); |
||||
} |
||||
if (slider.getPaintLabels()) { |
||||
centerSpacing -= getWidthOfWidestLabel(); |
||||
} |
||||
} |
||||
trackRect.x = contentRect.x + getWidthOfWidestLabel() + (contentRect.width - centerSpacing - 1) / 2; |
||||
trackRect.y = contentRect.y + trackBuffer; |
||||
trackRect.width = thumbRect.width; |
||||
trackRect.height = contentRect.height - (trackBuffer * 2); |
||||
} |
||||
|
||||
private void calHorizontalTrackRect() { |
||||
int centerSpacing; |
||||
centerSpacing = thumbRect.height; |
||||
if (slider.getPaintTicks()) { |
||||
centerSpacing += getTickLength(); |
||||
} |
||||
|
||||
if (slider.getPaintLabels()) { |
||||
centerSpacing += getHeightOfTallestLabel(); |
||||
} |
||||
trackRect.x = contentRect.x + trackBuffer; |
||||
trackRect.y = contentRect.y + getHeightOfTallestLabel() + (contentRect.height - centerSpacing - 1) / 2; |
||||
trackRect.width = contentRect.width - (trackBuffer * 2); |
||||
trackRect.height = thumbRect.height; |
||||
} |
||||
|
||||
@Override |
||||
protected int getHeightOfTallestLabel() { |
||||
Dictionary dictionary = slider.getLabelTable(); |
||||
int tallest = 0; |
||||
if (dictionary != null) { |
||||
Enumeration keys = dictionary.keys(); |
||||
while (keys.hasMoreElements()) { |
||||
JComponent label = (JComponent) dictionary.get(keys.nextElement()); |
||||
tallest = Math.max(label.getPreferredSize().height, tallest); |
||||
} |
||||
} |
||||
return Math.min(tallest, defaultLabelHeight); |
||||
} |
||||
|
||||
@Override |
||||
protected int getWidthOfWidestLabel() { |
||||
Dictionary dictionary = slider.getLabelTable(); |
||||
int widest = 0; |
||||
if (dictionary != null) { |
||||
Enumeration keys = dictionary.keys(); |
||||
while (keys.hasMoreElements()) { |
||||
JComponent label = (JComponent) dictionary.get(keys.nextElement()); |
||||
widest = Math.max(label.getPreferredSize().width, widest); |
||||
} |
||||
} |
||||
return Math.min(widest, defaultLabelHeight); |
||||
} |
||||
|
||||
/** |
||||
* Convenience function for determining ComponentOrientation. Helps us |
||||
* avoid having Munge directives throughout the code. |
||||
*/ |
||||
static boolean isLeftToRight(Component c) { |
||||
return c.getComponentOrientation().isLeftToRight(); |
||||
} |
||||
|
||||
@Override |
||||
public void paintThumb(Graphics g) { |
||||
Color thumbColor = getThumbColor(); |
||||
Color color = stateColor(slider, thumbHover, thumbPressed, thumbColor, disabledThumbColor, null, hoverThumbColor, pressedThumbColor); |
||||
color = FlatUIUtils.deriveColor(color, thumbColor); |
||||
|
||||
Color foreground = slider.getForeground(); |
||||
Color borderColor = (thumbBorderColor != null && foreground == defaultForeground) ? stateColor(slider, false, false, thumbBorderColor, disabledThumbBorderColor, focusedThumbBorderColor, null, null) : null; |
||||
|
||||
Color focusedColor = FlatUIUtils.deriveColor(this.focusedColor, (foreground != defaultForeground) ? foreground : focusBaseColor); |
||||
|
||||
paintThumb(g, slider, thumbRect, isRoundThumb(), color, borderColor, focusedColor, thumbBorderWidth, focusWidth); |
||||
} |
||||
|
||||
/** |
||||
* Paints the thumb. |
||||
* |
||||
* @param g the graphics context |
||||
* @param slider the slider |
||||
* @param thumbRect the thumb rectangle |
||||
* @param roundThumb whether the thumb should be round |
||||
* @param thumbColor the thumb color |
||||
* @param thumbBorderColor the thumb border color |
||||
* @param focusedColor the focused color |
||||
* @param thumbBorderWidth the thumb border width |
||||
* @param focusWidth the focus width |
||||
*/ |
||||
public static void paintThumb(Graphics g, JSlider slider, Rectangle thumbRect, boolean roundThumb, Color thumbColor, Color thumbBorderColor, Color focusedColor, float thumbBorderWidth, int focusWidth) { |
||||
double systemScaleFactor = UIScale.getSystemScaleFactor((Graphics2D) g); |
||||
int scaleFactor2 = 2; |
||||
if (systemScaleFactor != 1 && systemScaleFactor != scaleFactor2) { |
||||
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
||||
HiDPIUtils.paintAtScale1x((Graphics2D) g, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, (g2d, x2, y2, width2, height2, scaleFactor) -> { |
||||
paintThumbImpl(g, slider, x2, y2, width2, height2, roundThumb, thumbColor, thumbBorderColor, focusedColor, (float) (thumbBorderWidth * scaleFactor), (float) (focusWidth * scaleFactor)); |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
paintThumbImpl(g, slider, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, roundThumb, thumbColor, thumbBorderColor, focusedColor, thumbBorderWidth, focusWidth); |
||||
|
||||
} |
||||
|
||||
private static void paintThumbImpl(Graphics g, JSlider slider, int x, int y, int width, int height, boolean roundThumb, Color thumbColor, Color thumbBorderColor, Color focusedColor, float thumbBorderWidth, float focusWidth) { |
||||
int fw = Math.round(UIScale.scale(focusWidth)); |
||||
int tx = x + fw; |
||||
int ty = y + fw; |
||||
int tw = width - fw - fw; |
||||
int th = height - fw - fw; |
||||
boolean focused = FlatUIUtils.isPermanentFocusOwner(slider); |
||||
|
||||
if (roundThumb) { |
||||
paintRoundThumb(g, x, y, width, height, thumbColor, thumbBorderColor, focusedColor, thumbBorderWidth, focused, tx, ty, tw, th); |
||||
} else { |
||||
paintDirectionalThumb(g, slider, x, y, width, height, thumbColor, thumbBorderColor, focusedColor, thumbBorderWidth, tw, th, focused, fw); |
||||
} |
||||
} |
||||
|
||||
private static void paintDirectionalThumb(Graphics g, JSlider slider, int x, int y, int width, int height, Color thumbColor, Color thumbBorderColor, Color focusedColor, float thumbBorderWidth, int tw, int th, boolean focused, int fw) { |
||||
Graphics2D g2 = (Graphics2D) g.create(); |
||||
try { |
||||
g2.translate(x, y); |
||||
if (slider.getOrientation() == JSlider.VERTICAL) { |
||||
if (slider.getComponentOrientation().isLeftToRight()) { |
||||
g2.translate(0, height); |
||||
g2.rotate(Math.toRadians(270)); |
||||
} else { |
||||
g2.translate(width, 0); |
||||
g2.rotate(Math.toRadians(90)); |
||||
} |
||||
|
||||
// rotate thumb width/height
|
||||
int temp = tw; |
||||
tw = th; |
||||
th = temp; |
||||
} |
||||
|
||||
paintDirectionalThumbImpl(thumbColor, thumbBorderColor, focusedColor, thumbBorderWidth, tw, th, focused, fw, g2); |
||||
} finally { |
||||
g2.dispose(); |
||||
} |
||||
} |
||||
|
||||
private static void paintDirectionalThumbImpl(Color thumbColor, Color thumbBorderColor, Color focusedColor, float thumbBorderWidth, int tw, int th, boolean focused, int fw, Graphics2D g2) { |
||||
// paint thumb focus border
|
||||
if (focused) { |
||||
g2.setColor(focusedColor); |
||||
g2.fill(createDirectionalThumbShape(0, 0, tw + fw + fw, th + fw + fw, fw)); |
||||
} |
||||
|
||||
if (thumbBorderColor != null) { |
||||
// paint thumb border
|
||||
g2.setColor(thumbBorderColor); |
||||
g2.fill(createDirectionalThumbShape(fw, fw, tw, th, 0)); |
||||
|
||||
// paint thumb background
|
||||
float lw = UIScale.scale(thumbBorderWidth); |
||||
g2.setColor(thumbColor); |
||||
g2.fill(createDirectionalThumbShape(fw + lw, fw + lw, tw - lw - lw, th - lw - lw, 0)); |
||||
} else { |
||||
// paint thumb background
|
||||
g2.setColor(thumbColor); |
||||
g2.fill(createDirectionalThumbShape(fw, fw, tw, th, 0)); |
||||
} |
||||
} |
||||
|
||||
private static void paintRoundThumb(Graphics g, int x, int y, int width, int height, Color thumbColor, Color thumbBorderColor, Color focusedColor, float thumbBorderWidth, boolean focused, int tx, int ty, int tw, int th) { |
||||
// paint thumb focus border
|
||||
if (focused) { |
||||
g.setColor(focusedColor); |
||||
((Graphics2D) g).fill(createRoundThumbShape(x, y, width, height)); |
||||
} |
||||
|
||||
if (thumbBorderColor != null) { |
||||
// paint thumb border
|
||||
g.setColor(thumbBorderColor); |
||||
((Graphics2D) g).fill(createRoundThumbShape(tx, ty, tw, th)); |
||||
|
||||
// paint thumb background
|
||||
float lw = UIScale.scale(thumbBorderWidth); |
||||
g.setColor(thumbColor); |
||||
((Graphics2D) g).fill(createRoundThumbShape(tx + lw, ty + lw, tw - lw - lw, th - lw - lw)); |
||||
} else { |
||||
// paint thumb background
|
||||
g.setColor(thumbColor); |
||||
((Graphics2D) g).fill(createRoundThumbShape(tx, ty, tw, th)); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 无标签下创建圆形Thumb形状 |
||||
*/ |
||||
public static Shape createRoundThumbShape(float x, float y, float w, float h) { |
||||
if (AssistUtils.equals(w, h)) { |
||||
return new Ellipse2D.Float(x, y, w, h); |
||||
} else { |
||||
float arc = Math.min(w, h); |
||||
return new RoundRectangle2D.Float(x, y, w, h, arc, arc); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 有标签下创建Thumb形状 |
||||
*/ |
||||
public static Path2D createDirectionalThumbShape(float x, float y, float w, float h, float arc) { |
||||
|
||||
float wh = w / 2; |
||||
Path2D path = new Path2D.Float(Path2D.WIND_NON_ZERO, 9); |
||||
path.moveTo(x + wh, y); // 移到反转后的位置
|
||||
path.lineTo(x, y + wh); // 线到反转后的位置
|
||||
path.lineTo(x, y + h - arc); // 线到反转后的位置
|
||||
path.quadTo(x, y + h, x + arc, y + h); // 贝塞尔曲线到反转后的位置
|
||||
path.lineTo(x + (w - arc), y + h); // 线到反转后的位置
|
||||
path.quadTo(x + w, y + h, x + w, y + h - arc); // 贝塞尔曲线到反转后的位置
|
||||
path.lineTo(x + w, y + wh); // 线到反转后的位置
|
||||
path.closePath(); // 关闭路径
|
||||
|
||||
return path; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,393 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.icon.LazyIcon; |
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||
import com.fr.base.GraphHelper; |
||||
import com.fr.base.vcs.DesignerMode; |
||||
import com.fr.design.file.MultiTemplateTabPane; |
||||
import com.fr.stable.collections.combination.Pair; |
||||
|
||||
import javax.swing.Icon; |
||||
import javax.swing.JComponent; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import javax.swing.plaf.PanelUI; |
||||
import java.awt.Color; |
||||
import java.awt.Dimension; |
||||
import java.awt.FontMetrics; |
||||
import java.awt.GradientPaint; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.Insets; |
||||
import java.awt.geom.Path2D; |
||||
import java.awt.geom.Point2D; |
||||
import java.awt.geom.Rectangle2D; |
||||
|
||||
import static com.fine.theme.utils.FineUIScale.scale; |
||||
import static com.fine.theme.utils.FineUIUtils.paintRoundTabBorder; |
||||
import static com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; |
||||
import static com.fr.design.file.MultiTemplateTabPane.LEADING_WIDTH; |
||||
import static com.fr.design.file.MultiTemplateTabPane.TRAILING_WIDTH; |
||||
|
||||
/** |
||||
* 文件Tab栏UI |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/12/27 |
||||
*/ |
||||
public class FineTemplateTabPaneUI extends PanelUI { |
||||
private MultiTemplateTabPane tabPane; |
||||
|
||||
private static final String ELLIPSIS = "..."; |
||||
private static final int ICON_TEXT_GAP = 4; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color background; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color selectedBackground; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color hoverColor; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color closeIconHoverBackground; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color closeHoverBackground; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color borderColor; |
||||
|
||||
@Styleable(dot = true) |
||||
protected int tabHeight; |
||||
|
||||
@Styleable(dot = true) |
||||
protected int separatorHeight; |
||||
|
||||
@Styleable(dot = true) |
||||
protected int borderWidth; |
||||
|
||||
@Styleable(dot = true) |
||||
protected int tabArc; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Insets tabInsets; |
||||
|
||||
protected Icon fileIcon; |
||||
protected Icon moreAction; |
||||
protected Icon addAction; |
||||
protected Icon moreHoverAction; |
||||
|
||||
|
||||
private Icon closeIcon; |
||||
|
||||
private Icon closeHoverIcon; |
||||
|
||||
private int leadingWidth; |
||||
private int trailingWidth; |
||||
|
||||
protected FineTemplateTabPaneUI() { |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 创建UI |
||||
* |
||||
* @param c 组件 |
||||
* @return ComponentUI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineTemplateTabPaneUI(); |
||||
} |
||||
|
||||
@Override |
||||
public void installUI(JComponent c) { |
||||
super.installUI(c); |
||||
this.tabPane = (MultiTemplateTabPane) c; |
||||
closeIcon = new LazyIcon("clear"); |
||||
closeHoverIcon = new LazyIcon("clear_hover"); |
||||
addAction = new LazyIcon("add_worksheet"); |
||||
moreAction = new LazyIcon("tool_more"); |
||||
moreHoverAction = new LazyIcon("tool_more_hover"); |
||||
fileIcon = new LazyIcon("cpt_icon"); |
||||
leadingWidth = scale(LEADING_WIDTH); |
||||
trailingWidth = scale(TRAILING_WIDTH); |
||||
|
||||
borderWidth = FineUIUtils.getUIInt("TemplateTabPane.borderWidth", "TemplateTabPane.borderWidth"); |
||||
tabArc = FineUIUtils.getUIInt("TemplateTabPane.tabArc", "TemplateTabPane.tabArc"); |
||||
background = FineUIUtils.getUIColor("TemplateTabPane.background", "TabbedPane.background"); |
||||
selectedBackground = FineUIUtils.getUIColor("TemplateTabPane.selectedBackground", "TemplateTabPane.selectedBackground"); |
||||
closeHoverBackground = FineUIUtils.getUIColor("TemplateTabPane.closeHoverBackground", "TemplateTabPane.closeHoverBackground"); |
||||
borderColor = FineUIUtils.getUIColor("TemplateTabPane.borderColor", "TabbedPane.tabSeparatorColor"); |
||||
hoverColor = FineUIUtils.getUIColor("TemplateTabPane.hoverColor", "TemplateTabPane.hoverColor"); |
||||
closeIconHoverBackground = FineUIUtils.getUIColor("TemplateTabPane.icon.hoverBackground ", "TemplateTabPane.icon.hoverBackground "); |
||||
// ---- scaled ----
|
||||
tabInsets = FineUIUtils.getAndScaleUIInsets("TemplateTabPane.tabInsets", new Insets(4, 6, 4, 6)); |
||||
tabHeight = FineUIUtils.getAndScaleInt("TemplateTabPane.tabHeight", "TabbedPane.tabHeight"); |
||||
separatorHeight = FineUIUtils.getAndScaleInt("TemplateTabPane.separatorHeight", "TemplateTabPane.separatorHeight"); |
||||
} |
||||
|
||||
@Override |
||||
public void uninstallUI(JComponent c) { |
||||
super.uninstallUI(c); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void update(Graphics g, JComponent c) { |
||||
super.update(g, c); |
||||
double maxWidth = c.getWidth() - leadingWidth - trailingWidth; |
||||
Graphics2D g2d = (Graphics2D) g; |
||||
paintDefaultBackground(g2d); |
||||
paintPaneUnderLine(c.getWidth(), g2d); |
||||
paintTabs(g2d, maxWidth); |
||||
} |
||||
|
||||
private void paintDefaultBackground(Graphics2D g2d) { |
||||
//画默认背景
|
||||
g2d.setPaint(new GradientPaint(0, 0, tabPane.getBackground(), 1, (float) (tabHeight), tabPane.getBackground())); |
||||
g2d.fillRect(0, 0, tabPane.getWidth(), tabHeight); |
||||
} |
||||
|
||||
private void paintPaneUnderLine(float w, Graphics2D g2d) { |
||||
g2d.setPaint(borderColor); |
||||
float h = (float) tabHeight; |
||||
int t = scale(borderWidth); |
||||
Path2D border = new Path2D.Float(Path2D.WIND_EVEN_ODD); |
||||
border.append(FlatUIUtils.createComponentRectangle(0, 0, w, h, 0), false); |
||||
border.append(FlatUIUtils.createComponentRectangle(0, 0, w, h - t, 0), false); |
||||
g2d.fill(border); |
||||
} |
||||
|
||||
private void paintTabs(Graphics2D g2d, double maxWidth) { |
||||
|
||||
int maxStringlength = calculateStringMaxLength(); |
||||
if (tabPane.getSelectedIndex() >= tabPane.getTabCount()) { |
||||
tabPane.setSelectedIndex(tabPane.getTabCount() - 1); |
||||
} |
||||
if (tabPane.getSelectedIndex() < 0) { |
||||
tabPane.setSelectedIndex(0); |
||||
} |
||||
double templateStartX = leadingWidth; |
||||
|
||||
|
||||
//从可以开始展示在tab面板上的tab开始画
|
||||
Pair<Integer, Integer> viewRange = tabPane.getViewRange(); |
||||
for (int i = viewRange.getFirst(); i <= viewRange.getSecond(); i++) { |
||||
Icon icon = tabPane.getTemplateIconByIndex(i); |
||||
String name = tabPane.getTemplateShowNameByIndex(i); |
||||
//如果tab名字的长度大于最大能显示的英文字符长度,则进行省略号处理
|
||||
if (getStringWidth(name) > maxStringlength) { |
||||
name = getEllipsisName(name, maxStringlength); |
||||
} |
||||
|
||||
|
||||
Icon tabcloseIcon = tabPane.isCloseCurrent(i) ? closeHoverIcon : closeIcon; |
||||
if (i == tabPane.getSelectedIndex()) { |
||||
paintSelectedTab(g2d, icon, templateStartX, name, tabcloseIcon); |
||||
} else { |
||||
paintUnSelectedTab(g2d, icon, templateStartX, name, tabcloseIcon, |
||||
tabPane.getHoverIndex(), i); |
||||
} |
||||
templateStartX += tabPane.getTabWidth(); |
||||
} |
||||
|
||||
paintSeparators(g2d); |
||||
|
||||
if (!DesignerMode.isVcsMode()) { |
||||
paintTrailingAction(g2d, maxWidth); |
||||
} |
||||
} |
||||
|
||||
private void paintSeparators(Graphics2D g2d) { |
||||
g2d.setPaint(borderColor); |
||||
float x = leadingWidth; |
||||
Pair<Integer, Integer> viewRange = tabPane.getViewRange(); |
||||
for (int i = viewRange.getFirst(); i <= viewRange.getSecond(); i++) { |
||||
if (i != tabPane.getSelectedIndex() |
||||
&& i + 1 != tabPane.getSelectedIndex()) { |
||||
paintSeparator(g2d, x); |
||||
} |
||||
x += tabPane.getTabWidth(); |
||||
} |
||||
} |
||||
|
||||
|
||||
private void paintTrailingAction(Graphics2D g2d, double tabPaneWidth) { |
||||
int x = leadingWidth + (int) tabPaneWidth + (trailingWidth - moreAction.getIconWidth()) / 2; |
||||
int y = (tabHeight - moreAction.getIconHeight()) / 2; |
||||
if (tabPane.isHoverMoreAction()) { |
||||
moreHoverAction.paintIcon(tabPane, g2d, x, y); |
||||
} else { |
||||
moreAction.paintIcon(tabPane, g2d, x, y); |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 判断tab文字的长度大于能装下的最大文字长度,要用省略号 |
||||
* |
||||
* @param name |
||||
* @param maxStringlength |
||||
* @return |
||||
*/ |
||||
private String getEllipsisName(String name, int maxStringlength) { |
||||
|
||||
int ellipsisWidth = getStringWidth(ELLIPSIS); |
||||
int leftkeyPoint = 0; |
||||
int rightKeyPoint = name.length() - 1; |
||||
int leftStrWidth = 0; |
||||
int rightStrWidth = 0; |
||||
while (leftStrWidth + rightStrWidth + ellipsisWidth < maxStringlength) { |
||||
if (leftStrWidth <= rightStrWidth) { |
||||
leftkeyPoint++; |
||||
} else { |
||||
rightKeyPoint--; |
||||
} |
||||
leftStrWidth = getStringWidth(name.substring(0, leftkeyPoint)); |
||||
rightStrWidth = getStringWidth(name.substring(rightKeyPoint)); |
||||
|
||||
if (leftStrWidth + rightStrWidth + ellipsisWidth > maxStringlength) { |
||||
if (leftStrWidth <= rightStrWidth) { |
||||
rightKeyPoint++; |
||||
} else { |
||||
leftkeyPoint--; |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
return name.substring(0, leftkeyPoint) + ELLIPSIS + name.substring(rightKeyPoint); |
||||
} |
||||
|
||||
/** |
||||
* 计算过长度之后的每个tab的能接受的文字的英文字符数 |
||||
* |
||||
* @return |
||||
*/ |
||||
private int calculateStringMaxLength() { |
||||
return tabPane.getTabWidth() |
||||
- tabInsets.left - tabInsets.right |
||||
- ICON_TEXT_GAP * 2 |
||||
- fileIcon.getIconWidth() - closeIcon.getIconWidth(); |
||||
|
||||
} |
||||
|
||||
private int getStringWidth(String str) { |
||||
FontMetrics fm = GraphHelper.getFontMetrics(tabPane.getFont()); |
||||
int size = 0; |
||||
for (int i = 0; i < str.length(); i++) { |
||||
size += fm.charWidth(str.codePointAt(i)); |
||||
} |
||||
return size; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 画选中的tab |
||||
* |
||||
* @param g2d |
||||
* @param sheeticon |
||||
* @param templateStartX |
||||
* @param sheetName |
||||
* @param closeIcon |
||||
* @return |
||||
*/ |
||||
private void paintSelectedTab(Graphics2D g2d, Icon sheeticon, double templateStartX, String sheetName, Icon closeIcon) { |
||||
Object[] oriRenderingHints = FlatUIUtils.setRenderingHints(g2d); |
||||
// 绘制选中背景
|
||||
g2d.setPaint(selectedBackground); |
||||
Path2D tabShape = FineUIUtils.createTopRoundRectangle(templateStartX, 0, |
||||
tabPane.getTabWidth(), tabHeight, tabArc); |
||||
g2d.fill(tabShape); |
||||
// 绘制选中边框
|
||||
g2d.setPaint(borderColor); |
||||
paintRoundTabBorder(g2d, templateStartX, 0, |
||||
tabPane.getTabWidth(), tabHeight, borderWidth, (float) tabArc); |
||||
FlatUIUtils.resetRenderingHints(g2d, oriRenderingHints); |
||||
// 绘制图标
|
||||
int sheetIconY = (tabHeight - sheeticon.getIconHeight()) / 2; |
||||
sheeticon.paintIcon(tabPane, g2d, (int) templateStartX + tabInsets.left, sheetIconY); |
||||
// 绘制字符
|
||||
g2d.setPaint(tabPane.getForeground()); |
||||
Point2D.Double textPoint = calTextPoint(templateStartX, sheeticon.getIconWidth()); |
||||
g2d.drawString(sheetName, (int) textPoint.x, (int) textPoint.y); |
||||
int closePosition = (int) templateStartX + tabPane.getTabWidth() |
||||
- this.closeIcon.getIconWidth() - tabInsets.right; |
||||
int closeY = (tabHeight - closeIcon.getIconHeight()) / 2; |
||||
if (!DesignerMode.isVcsMode()) { |
||||
closeIcon.paintIcon(tabPane, g2d, closePosition, closeY); |
||||
} |
||||
} |
||||
|
||||
private Point2D.Double calTextPoint(double x, int iconWidth) { |
||||
FontMetrics fm = tabPane.getFontMetrics(tabPane.getFont()); |
||||
int ascent = fm.getAscent(); |
||||
int gap = (tabHeight - tabInsets.top - tabInsets.bottom - ascent) / 2; |
||||
double y = tabInsets.top + ascent + gap; |
||||
return new Point2D.Double(x + iconWidth + tabInsets.left + ICON_TEXT_GAP, y); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 画没有选中的tab |
||||
* |
||||
* @param g2d |
||||
* @param sheeticon |
||||
* @param templateStartX |
||||
* @param sheetName |
||||
* @param closeIcon |
||||
*/ |
||||
private void paintUnSelectedTab(Graphics2D g2d, Icon sheeticon, double templateStartX, String sheetName, Icon closeIcon, int mouseOveredIndex, int selfIndex) { |
||||
if (selfIndex == mouseOveredIndex) { |
||||
g2d.setPaint(hoverColor); |
||||
} else { |
||||
g2d.setPaint(background); |
||||
} |
||||
|
||||
Object[] oriRenderingHints = FlatUIUtils.setRenderingHints(g2d); |
||||
|
||||
Path2D tabShape = FineUIUtils.createTopRoundRectangle(templateStartX, 0, |
||||
tabPane.getTabWidth(), tabHeight - scale(borderWidth), tabArc); |
||||
g2d.fill(tabShape); |
||||
|
||||
|
||||
FlatUIUtils.resetRenderingHints(g2d, oriRenderingHints); |
||||
|
||||
int sheetIconY = (tabHeight - sheeticon.getIconHeight()) / 2; |
||||
sheeticon.paintIcon(tabPane, g2d, (int) templateStartX + tabInsets.left, sheetIconY); |
||||
// 画字符
|
||||
g2d.setPaint(tabPane.getForeground()); |
||||
Point2D.Double textPoint = calTextPoint(templateStartX, sheeticon.getIconWidth()); |
||||
g2d.drawString(sheetName, (int) textPoint.x, (int) textPoint.y); |
||||
int closeY = (tabHeight - closeIcon.getIconHeight()) / 2; |
||||
int closePosition = (int) templateStartX + tabPane.getTabWidth() |
||||
- this.closeIcon.getIconWidth() - tabInsets.right; |
||||
if (!DesignerMode.isVcsMode()) { |
||||
closeIcon.paintIcon(tabPane, g2d, closePosition, closeY); |
||||
} |
||||
} |
||||
|
||||
private void paintSeparator(Graphics2D g2d, float templateStartX) { |
||||
float x = templateStartX + tabPane.getTabWidth(); |
||||
float gap = (tabHeight - separatorHeight) / 2.0f; |
||||
g2d.fill(new Rectangle2D.Float(x, gap, scale(borderWidth), tabHeight - gap * 2)); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Dimension getPreferredSize(JComponent c) { |
||||
return new Dimension(c.getWidth(), tabHeight); |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getMinimumSize(JComponent c) { |
||||
return new Dimension(0, tabHeight); |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getMaximumSize(JComponent c) { |
||||
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); |
||||
} |
||||
} |
@ -0,0 +1,213 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
|
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; |
||||
import com.formdev.flatlaf.ui.FlatToggleButtonUI; |
||||
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||
import com.fr.design.gui.ibutton.UIButton; |
||||
import org.jetbrains.annotations.NotNull; |
||||
import org.jetbrains.annotations.Nullable; |
||||
|
||||
import javax.swing.AbstractButton; |
||||
import javax.swing.JComponent; |
||||
import javax.swing.JToggleButton; |
||||
import javax.swing.UIManager; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.Color; |
||||
import java.awt.Component; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.Shape; |
||||
import java.awt.Rectangle; |
||||
|
||||
import static com.fine.theme.utils.FineClientProperties.BUTTON_TYPE_GROUP; |
||||
import static com.fine.theme.utils.FineClientProperties.BUTTON_TYPE; |
||||
import static com.fine.theme.utils.FineClientProperties.BUTTON_TYPE_TAB; |
||||
import static com.fine.theme.utils.FineClientProperties.BUTTON_GROUP_POSITION; |
||||
import static com.fine.theme.utils.FineClientProperties.GROUP_BUTTON_POSITION_INNER; |
||||
import static com.fine.theme.utils.FineClientProperties.GROUP_BUTTON_POSITION_LEFT; |
||||
import static com.fine.theme.utils.FineClientProperties.GROUP_BUTTON_POSITION_LEFT_BOTTOM; |
||||
import static com.fine.theme.utils.FineClientProperties.GROUP_BUTTON_POSITION_LEFT_TOP; |
||||
import static com.fine.theme.utils.FineClientProperties.GROUP_BUTTON_POSITION_RIGHT; |
||||
import static com.fine.theme.utils.FineClientProperties.GROUP_BUTTON_POSITION_RIGHT_BOTTOM; |
||||
import static com.fine.theme.utils.FineClientProperties.GROUP_BUTTON_POSITION_RIGHT_TOP; |
||||
import static com.fine.theme.utils.FineClientProperties.TAB_BUTTON_SELECTED_BACKGROUND; |
||||
import static com.formdev.flatlaf.FlatClientProperties.clientPropertyColor; |
||||
import static com.formdev.flatlaf.FlatClientProperties.clientPropertyInt; |
||||
|
||||
/** |
||||
* 提供 {@link javax.swing.JToggleButton} 的UI类 |
||||
* <p> |
||||
* |
||||
* @author vito |
||||
* @uiDefault ToggleButton.tab.arc int |
||||
* @since 11.0 |
||||
* Created on 2023/11/3 |
||||
*/ |
||||
public class FineToggleButtonUI extends FlatToggleButtonUI { |
||||
|
||||
@Styleable(dot = true) |
||||
protected int tabArc; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color groupBackground; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color groupSelectedBackground; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color groupSelectedForeground; |
||||
|
||||
public static ComponentUI createUI(JComponent c) { |
||||
return FlatUIUtils.canUseSharedUI(c) |
||||
? FlatUIUtils.createSharedUI(FlatToggleButtonUI.class, () -> new FineToggleButtonUI(true)) |
||||
: new FineToggleButtonUI(false); |
||||
} |
||||
|
||||
protected FineToggleButtonUI(boolean shared) { |
||||
super(shared); |
||||
} |
||||
|
||||
@Override |
||||
protected void installDefaults(AbstractButton b) { |
||||
super.installDefaults(b); |
||||
tabArc = UIManager.getInt("ToggleButton.tab.arc"); |
||||
groupBackground = FineUIUtils.getUIColor("ToggleButton.group.background", "ToggleButton.background"); |
||||
groupSelectedBackground = FineUIUtils.getUIColor("ToggleButton.group.selectedBackground", "ToggleButton.selectedBackground"); |
||||
groupSelectedForeground = FineUIUtils.getUIColor("ToggleButton.group.selectedForeground", "ToggleButton.selectedForeground"); |
||||
} |
||||
|
||||
|
||||
@Nullable |
||||
static String getButtonTypeStr(AbstractButton c) { |
||||
Object value = c.getClientProperty(BUTTON_TYPE); |
||||
if (value instanceof String) |
||||
return (String) value; |
||||
return null; |
||||
} |
||||
|
||||
static int getGroupButtonPosition(AbstractButton c) { |
||||
return clientPropertyInt(c, BUTTON_GROUP_POSITION, GROUP_BUTTON_POSITION_INNER); |
||||
} |
||||
|
||||
static boolean isTabButton(Component c) { |
||||
return c instanceof JToggleButton && BUTTON_TYPE_TAB.equals(getButtonTypeStr((JToggleButton) c)); |
||||
} |
||||
|
||||
static boolean isGroupButton(Component c) { |
||||
return c instanceof UIButton && BUTTON_TYPE_GROUP.equals(getButtonTypeStr((UIButton) c)); |
||||
} |
||||
|
||||
@Override |
||||
public void paint(Graphics g, JComponent c) { |
||||
if (isGroupButton(c) || isTabButton(c)) { |
||||
((AbstractButton)c).setMargin(FineUIUtils.getUIInsets("ToggleButton.compact.margin", "ToggleButton.margin")); |
||||
} |
||||
super.paint(g, c); |
||||
} |
||||
|
||||
@Override |
||||
protected void paintBackground(Graphics g, JComponent c) { |
||||
if (isTabButton(c)) { |
||||
paintTabButton(g, c); |
||||
} else if (isGroupButton(c)) { |
||||
paintGroupButton(g, c); |
||||
} else { |
||||
super.paintBackground(g, c); |
||||
} |
||||
} |
||||
|
||||
protected void paintTabButton(Graphics g, JComponent c) { |
||||
int height = c.getHeight(); |
||||
int width = c.getWidth(); |
||||
boolean selected = ((AbstractButton) c).isSelected(); |
||||
Color enabledColor = selected ? clientPropertyColor(c, TAB_BUTTON_SELECTED_BACKGROUND, tabSelectedBackground) : null; |
||||
|
||||
// use component background if explicitly set
|
||||
if (enabledColor == null) { |
||||
Color bg = c.getBackground(); |
||||
if (isCustomBackground(bg)) { |
||||
enabledColor = bg; |
||||
} |
||||
} |
||||
|
||||
// paint background
|
||||
Color background = buttonStateColor(c, enabledColor, |
||||
null, tabFocusBackground, tabHoverBackground, null); |
||||
if (background != null) { |
||||
g.setColor(background); |
||||
g.fillRoundRect(0, 0, width, height, tabArc, tabArc); |
||||
} |
||||
} |
||||
|
||||
protected void paintGroupButton(Graphics g, JComponent c) { |
||||
Color background = getBackground(c); |
||||
if (background == null) { |
||||
return; |
||||
} |
||||
Graphics2D g2 = (Graphics2D) g.create(); |
||||
try { |
||||
FlatUIUtils.setRenderingHints(g2); |
||||
g2.setColor(FlatUIUtils.deriveColor(background, getBackgroundBase(c, true))); |
||||
|
||||
int position = getGroupButtonPosition((AbstractButton) c); |
||||
if (position == GROUP_BUTTON_POSITION_INNER) { |
||||
float focusWidth = FlatUIUtils.getBorderFocusWidth(c); |
||||
FlatUIUtils.paintComponentBackground(g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, 0); |
||||
} else { |
||||
float arc = FlatUIUtils.getBorderArc( c ) / 2; |
||||
Shape path2D = getGroupButtonPath2D(c, position, arc); |
||||
g2.fill(path2D); |
||||
} |
||||
} finally { |
||||
g2.dispose(); |
||||
} |
||||
} |
||||
|
||||
@NotNull |
||||
private static Shape getGroupButtonPath2D(JComponent c, int position, float arc) { |
||||
Shape path2D; |
||||
switch (position) { |
||||
case GROUP_BUTTON_POSITION_LEFT: |
||||
path2D = FineUIUtils.createLeftRoundRectangle(0, 0, c.getWidth(), c.getHeight(), arc); |
||||
break; |
||||
case GROUP_BUTTON_POSITION_RIGHT: |
||||
path2D = FineUIUtils.createRightRoundRectangle(0, 0, c.getWidth(), c.getHeight(), arc); |
||||
break; |
||||
case GROUP_BUTTON_POSITION_LEFT_TOP: |
||||
path2D = FineUIUtils.createTopLeftRoundRectangle(0, 0, c.getWidth(), c.getHeight(), arc); |
||||
break; |
||||
case GROUP_BUTTON_POSITION_LEFT_BOTTOM: |
||||
path2D = FineUIUtils.createBottomLeftRoundRectangle(0, 0, c.getWidth(), c.getHeight(), arc); |
||||
break; |
||||
case GROUP_BUTTON_POSITION_RIGHT_TOP: |
||||
path2D = FineUIUtils.createTopRightRoundRectangle(0, 0, c.getWidth(), c.getHeight(), arc); |
||||
break; |
||||
case GROUP_BUTTON_POSITION_RIGHT_BOTTOM: |
||||
path2D = FineUIUtils.createBottomRightRoundRectangle(0, 0, c.getWidth(), c.getHeight(), arc); |
||||
break; |
||||
default: |
||||
path2D = new Rectangle(); |
||||
} |
||||
return path2D; |
||||
} |
||||
|
||||
@Override |
||||
protected Color getForeground(JComponent c) { |
||||
if (isGroupButton(c) && ((AbstractButton)c).isSelected()) { |
||||
return groupSelectedForeground; |
||||
} |
||||
return super.getForeground(c); |
||||
} |
||||
|
||||
@Override |
||||
protected Color getBackground(JComponent c) { |
||||
if (isGroupButton(c)) { |
||||
return ((AbstractButton)c).isSelected() ? groupSelectedBackground : groupBackground; |
||||
} |
||||
return super.getBackground(c); |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,102 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.utils.FineUIScale; |
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatToolTipUI; |
||||
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||
import com.fr.base.BaseUtils; |
||||
import com.fr.stable.Constants; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.JToolTip; |
||||
import javax.swing.SwingUtilities; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.Dimension; |
||||
import java.awt.FontMetrics; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.Insets; |
||||
import java.awt.RenderingHints; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* ToolTip UI类 |
||||
* |
||||
* @author Leo.Qin |
||||
* @since 11.0 |
||||
* Created on 2023/12/8 |
||||
*/ |
||||
public class FineTooltipUI extends FlatToolTipUI { |
||||
private final int defaultMaxWidth = 392; |
||||
private final int defaultArc = 5; |
||||
private int maxWidth; |
||||
private int arc; |
||||
private List<String> lines; |
||||
|
||||
/** |
||||
* 创建UI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return FlatUIUtils.createSharedUI(FineTooltipUI.class, FineTooltipUI::new); |
||||
} |
||||
|
||||
@Override |
||||
public void paint(Graphics g, JComponent c) { |
||||
Graphics2D g2d = (Graphics2D) g; |
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||
|
||||
g2d.setColor(c.getBackground()); |
||||
g2d.fillRoundRect(0, 0, c.getWidth(), c.getHeight(), arc, arc); |
||||
|
||||
String text = ((JToolTip) c).getTipText(); |
||||
if (text == null || text.isEmpty()) { |
||||
return; |
||||
} |
||||
|
||||
Insets insets = c.getInsets(); |
||||
FontMetrics fm = g2d.getFontMetrics(); |
||||
|
||||
int x = insets.left; |
||||
int y = insets.top + fm.getAscent(); |
||||
|
||||
g2d.setColor(c.getForeground()); |
||||
|
||||
for (String line : lines) { |
||||
g2d.drawString(line, x, y); |
||||
y += fm.getHeight(); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected void installDefaults(JComponent c) { |
||||
super.installDefaults(c); |
||||
c.setOpaque(false); |
||||
arc = FineUIScale.scale(FineUIUtils.getAndScaleInt("ToolTip.arc", defaultArc)); |
||||
maxWidth = FineUIUtils.getAndScaleInt("ToolTip.maxWidth", defaultMaxWidth); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Dimension getPreferredSize(JComponent c) { |
||||
String text = ((JToolTip) c).getTipText(); |
||||
if (text == null || text.isEmpty()) { |
||||
return new Dimension(); |
||||
} |
||||
|
||||
Insets insets = c.getInsets(); |
||||
int fontWidth = this.maxWidth - insets.left - insets.right; |
||||
lines = BaseUtils.getLineTextList(text, null, null, fontWidth, Constants.FR_PAINT_RESOLUTION); |
||||
|
||||
FontMetrics fm = c.getFontMetrics(c.getFont()); |
||||
|
||||
int width = 0; |
||||
int height = fm.getHeight() * Math.max(lines.size(), 1); |
||||
for (String line : lines) { |
||||
width = Math.max(width, SwingUtilities.computeStringWidth(fm, line)); |
||||
} |
||||
|
||||
return new Dimension(insets.left + width + insets.right, insets.top + height + insets.bottom); |
||||
} |
||||
} |
@ -0,0 +1,39 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.formdev.flatlaf.ui.FlatButtonUI; |
||||
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||
|
||||
import javax.swing.JComponent; |
||||
import java.awt.*; |
||||
|
||||
/** |
||||
* 矩形按钮UI,忽略圆角属性 |
||||
* |
||||
* @author Levy.Xie |
||||
* @since 11.0 |
||||
* Created on 2023/11/30 |
||||
*/ |
||||
public class RectangleButtonUI extends FlatButtonUI { |
||||
|
||||
|
||||
public RectangleButtonUI(boolean shared) { |
||||
super(shared); |
||||
} |
||||
|
||||
@Override |
||||
protected void paintBackground(Graphics g, JComponent c) { |
||||
Color background = getBackground(c); |
||||
if (background == null) { |
||||
return; |
||||
} |
||||
Graphics2D g2 = (Graphics2D) g.create(); |
||||
try { |
||||
FlatUIUtils.setRenderingHints(g2); |
||||
g2.setColor(FlatUIUtils.deriveColor(background, getBackgroundBase(c, true))); |
||||
float focusWidth = FlatUIUtils.getBorderFocusWidth(c); |
||||
FlatUIUtils.paintComponentBackground(g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, 0); |
||||
} finally { |
||||
g2.dispose(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,92 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.formdev.flatlaf.ui.FlatScrollBarUI; |
||||
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.JButton; |
||||
import javax.swing.UIManager; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.Color; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
|
||||
/** |
||||
* 应用于主面板报表工作区的滚动条UI,提供给 {@link com.fr.design.cell.bar.DynamicScrollBar} 的UI类 |
||||
* |
||||
* @author Levy.Xie |
||||
* @since 11.0` |
||||
* Created on 2023/12/08 |
||||
*/ |
||||
public class ReportScrollBarUI extends FlatScrollBarUI { |
||||
|
||||
/** |
||||
* 创建UI类 |
||||
* |
||||
* @param c 组件 |
||||
* @return ReportScrollBarUI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new ReportScrollBarUI(); |
||||
} |
||||
|
||||
@Override |
||||
public void installUI(JComponent c) { |
||||
super.installUI(c); |
||||
scrollBarWidth = UIManager.getInt("ScrollBar.largeBar.width"); |
||||
thumbInsets = UIManager.getInsets("ScrollBar.largeBar.thumbInsets"); |
||||
showButtons = UIManager.getBoolean("ScrollBar.largeBar.showButtons"); |
||||
trackColor = UIManager.getColor("ScrollBar.largeBar.track"); |
||||
} |
||||
|
||||
@Override |
||||
public void uninstallUI(JComponent c) { |
||||
super.uninstallUI(c); |
||||
} |
||||
|
||||
@Override |
||||
protected JButton createDecreaseButton(int orientation) { |
||||
return new ReportScrollBarButton(orientation); |
||||
} |
||||
|
||||
@Override |
||||
protected JButton createIncreaseButton(int orientation) { |
||||
return new ReportScrollBarButton(orientation); |
||||
} |
||||
|
||||
protected class ReportScrollBarButton extends FlatScrollBarButton { |
||||
|
||||
protected final Color defaultButtonBackground = UIManager.getColor("ScrollBar.largeBar.buttonBackground"); |
||||
|
||||
protected ReportScrollBarButton(int direction) { |
||||
super(direction); |
||||
} |
||||
|
||||
@Override |
||||
public void paint(Graphics g) { |
||||
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints(g); |
||||
|
||||
// paint hover or pressed background
|
||||
if (isEnabled()) { |
||||
Color background = (pressedBackground != null && isPressed()) |
||||
? pressedBackground |
||||
: (hoverBackground != null && isHover() |
||||
? hoverBackground |
||||
: null); |
||||
|
||||
if (background == null) { |
||||
background = defaultButtonBackground; |
||||
} |
||||
g.setColor(deriveBackground(background)); |
||||
paintBackground((Graphics2D) g); |
||||
} |
||||
|
||||
// paint arrow
|
||||
g.setColor(deriveForeground(getArrowColor())); |
||||
paintArrow((Graphics2D) g); |
||||
|
||||
FlatUIUtils.resetRenderingHints(g, oldRenderingHints); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,32 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.icon.LazyIcon; |
||||
import com.formdev.flatlaf.ui.FlatTreeUI; |
||||
|
||||
import javax.swing.JComponent; |
||||
import javax.swing.plaf.ComponentUI; |
||||
|
||||
/** |
||||
* 主题化的TreeUI,继承自FlatTreeUI |
||||
* |
||||
* @author Levy.Xie |
||||
* @since 11.0 |
||||
* Created on 2023/11/29 |
||||
*/ |
||||
public class UIFlatTreeUI extends FlatTreeUI { |
||||
|
||||
/** |
||||
* 创建组件UI |
||||
* @param x 组件 |
||||
* @return 返回组件UI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent x) { |
||||
return new UIFlatTreeUI(); |
||||
} |
||||
|
||||
protected void installDefaults() { |
||||
super.installDefaults(); |
||||
setExpandedIcon(new LazyIcon("minus")); |
||||
setCollapsedIcon(new LazyIcon("plus")); |
||||
} |
||||
} |
@ -0,0 +1,52 @@
|
||||
package com.fine.theme.light.ui.laf; |
||||
|
||||
import com.fine.swing.ui.layout.Layouts; |
||||
import com.fine.theme.icon.IconManager; |
||||
import com.fine.theme.light.ui.FineLightIconSet; |
||||
import com.formdev.flatlaf.util.UIScale; |
||||
import com.fr.stable.StringUtils; |
||||
|
||||
/** |
||||
* Fine 暗色主题 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/9/12 |
||||
*/ |
||||
public class FineDarkLaf extends FineLaf { |
||||
|
||||
private static final String USER_SCALE_FACTOR = "userScaleFactor"; |
||||
|
||||
private static final String NAME = "FineLaf Dark"; |
||||
|
||||
/** |
||||
* 安装外观 |
||||
* |
||||
* @return 是否安装成功 |
||||
*/ |
||||
public static boolean setup() { |
||||
IconManager.addSet(new FineLightIconSet("fine-dark")); |
||||
Layouts.setScaleFactor(UIScale.getUserScaleFactor()); |
||||
UIScale.addPropertyChangeListener(evt -> { |
||||
if (StringUtils.equals(evt.getPropertyName(), USER_SCALE_FACTOR)) { |
||||
Layouts.setScaleFactor((float) evt.getNewValue()); |
||||
} |
||||
}); |
||||
return setup(new FineDarkLaf()); |
||||
} |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return NAME; |
||||
} |
||||
|
||||
@Override |
||||
public String getDescription() { |
||||
return "Fine New Dark Look and Feel"; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isDark() { |
||||
return true; |
||||
} |
||||
} |
@ -0,0 +1,38 @@
|
||||
package com.fine.theme.light.ui.laf; |
||||
|
||||
import com.formdev.flatlaf.FlatLaf; |
||||
|
||||
import javax.swing.PopupFactory; |
||||
|
||||
/** |
||||
* Fine designer new look and feel |
||||
* FineLaf.properties 定义公共属性,如UI等, |
||||
* 其他主题继承该主题进行自定义,防止出现UI不存在等问题 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/12/18 |
||||
*/ |
||||
public abstract class FineLaf extends FlatLaf { |
||||
|
||||
private static final String NAME = "FineLaf"; |
||||
|
||||
|
||||
@Override |
||||
public String getName() { |
||||
return NAME; |
||||
} |
||||
|
||||
@Override |
||||
public String getDescription() { |
||||
return "Fine New Look and Feel"; |
||||
} |
||||
|
||||
@Override |
||||
public void initialize() { |
||||
super.initialize(); |
||||
// flat默认使用系统弹窗,3.3 版本之前无法实现圆角弹窗。
|
||||
// popup弹窗不使用flat提供的工具,使用swing原生自带的
|
||||
PopupFactory.setSharedInstance(new PopupFactory()); |
||||
} |
||||
} |
@ -0,0 +1,52 @@
|
||||
package com.fine.theme.light.ui.laf; |
||||
|
||||
import com.fine.swing.ui.layout.Layouts; |
||||
import com.fine.theme.icon.IconManager; |
||||
import com.fine.theme.light.ui.FineLightIconSet; |
||||
import com.formdev.flatlaf.util.UIScale; |
||||
import com.fr.stable.StringUtils; |
||||
|
||||
/** |
||||
* Fine 亮色主题 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/9/12 |
||||
*/ |
||||
public class FineLightLaf extends FineLaf { |
||||
|
||||
public static final String USER_SCALE_FACTOR = "userScaleFactor"; |
||||
|
||||
private static final String NAME = "FineLaf Light"; |
||||
|
||||
/** |
||||
* 安装外观 |
||||
* |
||||
* @return 是否安装成功 |
||||
*/ |
||||
public static boolean setup() { |
||||
IconManager.addSet(new FineLightIconSet("fine-light")); |
||||
Layouts.setScaleFactor(UIScale.getUserScaleFactor()); |
||||
UIScale.addPropertyChangeListener(evt -> { |
||||
if (StringUtils.equals(evt.getPropertyName(), USER_SCALE_FACTOR)) { |
||||
Layouts.setScaleFactor((float) evt.getNewValue()); |
||||
} |
||||
}); |
||||
return setup(new FineLightLaf()); |
||||
} |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return NAME; |
||||
} |
||||
|
||||
@Override |
||||
public String getDescription() { |
||||
return "Fine New Light Look and Feel"; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isDark() { |
||||
return false; |
||||
} |
||||
} |
@ -0,0 +1,35 @@
|
||||
package com.fine.theme.utils; |
||||
|
||||
import com.formdev.flatlaf.FlatClientProperties; |
||||
|
||||
/** |
||||
* FR-UI中使用的各类属性 |
||||
* |
||||
* @author Levy.Xie |
||||
* @since 11.0 |
||||
* Created on 2023/12/15 |
||||
*/ |
||||
public interface FineClientProperties extends FlatClientProperties { |
||||
|
||||
//--------------------------- ButtonGroup -----------------------
|
||||
String BUTTON_TYPE_GROUP = "group"; |
||||
|
||||
String BUTTON_BORDER = "buttonBorder"; |
||||
String BUTTON_BORDER_LEFT_ROUND_RECT = "leftRoundRect"; |
||||
String BUTTON_BORDER_RIGHT_ROUND_RECT = "rightRoundRect"; |
||||
|
||||
//--------------------------- PopupMenu -----------------------
|
||||
String MENU_ITEM_TYPE = "MenuItemType"; |
||||
String MENU_ITEM_TYPE_LOCK = "lock"; |
||||
|
||||
String BUTTON_GROUP_POSITION = "group_position"; |
||||
|
||||
int GROUP_BUTTON_POSITION_INNER = 0; |
||||
int GROUP_BUTTON_POSITION_LEFT = 1; |
||||
int GROUP_BUTTON_POSITION_RIGHT = 2; |
||||
int GROUP_BUTTON_POSITION_LEFT_TOP = 3; |
||||
int GROUP_BUTTON_POSITION_LEFT_BOTTOM = 4; |
||||
int GROUP_BUTTON_POSITION_RIGHT_TOP = 5; |
||||
int GROUP_BUTTON_POSITION_RIGHT_BOTTOM = 6; |
||||
|
||||
} |
@ -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,101 @@
|
||||
package com.fine.theme.utils; |
||||
|
||||
import com.finebi.cbb.utils.StringUtils; |
||||
|
||||
import javax.swing.JComponent; |
||||
|
||||
/** |
||||
* UI样式工具 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2024/1/4 |
||||
*/ |
||||
public interface FineUIStyle { |
||||
|
||||
String IN_TOOLBAR_GROUP = "inToolbarGroup"; |
||||
String STYLE_PRIMARY = "primary"; |
||||
String STYLE_SECONDARY = "secondary"; |
||||
String STYLE_TEXT = "text"; |
||||
String STYLE_SIZE_MEDIUM = "mediumSize"; |
||||
String STYLE_SIZE_SMALL = "smallSize"; |
||||
String MENU_BAR = "menuBar"; |
||||
String LIGHT_GREY = "lightGrey"; |
||||
String IN_TOOLBAR_LEFT = "inToolbarLeft"; |
||||
String IN_TOOLBAR_RIGHT = "inToolbarRight"; |
||||
String NORMAL_COLOR = "normalColor"; |
||||
String TOP_TOOLS = "topTools"; |
||||
String BRAND_COLOR_LABEL = "brandColorLabel"; |
||||
String BUTTON_TAB_ACTION = "tabAction"; |
||||
|
||||
String MENU_TOOL_BAR = "menuToolBar"; |
||||
String MENU_ITEM_TOOL_BAR = "menuItemToolBar"; |
||||
String POPUP_MENU_TOOL_BAR = "popupMenuToolBar"; |
||||
|
||||
|
||||
/** |
||||
* 添加组件的样式类,类似css,该方法会接在原样式后方 |
||||
* <code> |
||||
* FineClientProperties.appendStyle("primary small") |
||||
* </code> |
||||
* |
||||
* @param it 组件 |
||||
* @param styleClass 样式字符串,支持连续添加类,用空格 |
||||
*/ |
||||
static void appendStyle(JComponent it, String styleClass) { |
||||
Object oriProperty = it.getClientProperty(FineClientProperties.STYLE_CLASS); |
||||
if (oriProperty instanceof String && StringUtils.isNotBlank((String) oriProperty)) { |
||||
styleClass = oriProperty + " " + styleClass; |
||||
} |
||||
it.putClientProperty(FineClientProperties.STYLE_CLASS, styleClass); |
||||
} |
||||
|
||||
/** |
||||
* 设置组件的样式类,类似css,该方法会替换原样式 |
||||
* <code> |
||||
* FineClientProperties.setStyle("primary small") |
||||
* </code> |
||||
* |
||||
* @param jComponent 组件 |
||||
* @param styleClass 样式字符串,支持连续添加类,用空格 |
||||
*/ |
||||
static void setStyle(JComponent jComponent, String styleClass) { |
||||
jComponent.putClientProperty(FineClientProperties.STYLE_CLASS, styleClass); |
||||
} |
||||
|
||||
/** |
||||
* 样式组合 |
||||
* |
||||
* @param styleClasses 所有样式 |
||||
* @return 样式列表 |
||||
*/ |
||||
static String joinStyle(String... styleClasses) { |
||||
final StringBuilder sb = new StringBuilder(); |
||||
for (final String style : styleClasses) { |
||||
if (style == null) { |
||||
continue; |
||||
} |
||||
if (sb.length() > 0) { |
||||
sb.append(" "); |
||||
} |
||||
sb.append(style); |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
|
||||
/** |
||||
* 包含样式 |
||||
* |
||||
* @param jComponent 组件 |
||||
* @param styleClass 样式 |
||||
* @return 是否包含指定的样式 |
||||
*/ |
||||
static boolean hasStyle(JComponent jComponent, String styleClass) { |
||||
Object style = jComponent.getClientProperty(FineClientProperties.STYLE_CLASS); |
||||
if (style instanceof String && StringUtils.isNotBlank((String) style)) { |
||||
return ((String) style).contains(styleClass); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,367 @@
|
||||
package com.fine.theme.utils; |
||||
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||
import com.fr.stable.os.OperatingSystem; |
||||
import com.fr.value.AtomicClearableLazyValue; |
||||
|
||||
import javax.swing.UIManager; |
||||
import java.awt.Color; |
||||
import java.awt.Component; |
||||
import java.awt.Composite; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.GraphicsDevice; |
||||
import java.awt.GraphicsEnvironment; |
||||
import java.awt.Insets; |
||||
import java.awt.geom.Path2D; |
||||
import java.awt.geom.RoundRectangle2D; |
||||
import java.lang.reflect.Field; |
||||
|
||||
import static com.fine.theme.light.ui.FineButtonUI.isLeftRoundButton; |
||||
import static com.formdev.flatlaf.util.UIScale.scale; |
||||
|
||||
/** |
||||
* 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); |
||||
} |
||||
|
||||
/** |
||||
* 获取key指定的int值,并根据dpi进行缩放 |
||||
* |
||||
* @param key int所在的key |
||||
* @param defaultKey 后备key |
||||
* @return 长度 |
||||
*/ |
||||
public static int getAndScaleInt(String key, String defaultKey) { |
||||
int intNum = getUIInt(key, defaultKey); |
||||
return FineUIScale.scale(intNum); |
||||
} |
||||
|
||||
/** |
||||
* 获取key指定的int值,并根据dpi进行缩放 |
||||
* |
||||
* @param key int所在的key |
||||
* @param defaultInt 默认值 |
||||
* @return 长度 |
||||
*/ |
||||
public static int getAndScaleInt(String key, int defaultInt) { |
||||
int intNum = FlatUIUtils.getUIInt(key, defaultInt); |
||||
return FineUIScale.scale(intNum); |
||||
} |
||||
|
||||
/** |
||||
* 通过key获取UI的边距,如果没有则使用后备key获取 |
||||
* |
||||
* @param key 边距key |
||||
* @param defaultKey 边距后备key |
||||
* @return 边距 |
||||
*/ |
||||
public static Insets getUIInsets(String key, String defaultKey) { |
||||
Insets margin = UIManager.getInsets(key); |
||||
return (margin != null) ? margin : UIManager.getInsets(defaultKey); |
||||
} |
||||
|
||||
/** |
||||
* 通过key获取UI的边距,如果没有则使用后备边距 |
||||
* |
||||
* @param key 边距key |
||||
* @param defaultInsets 后备边距 |
||||
* @return 边距 |
||||
*/ |
||||
public static Insets getUIInsets(String key, Insets defaultInsets) { |
||||
Insets margin = UIManager.getInsets(key); |
||||
return (margin != null) ? margin : defaultInsets; |
||||
} |
||||
|
||||
/** |
||||
* 通过key获取UI的边距,如果没有则使用后备边距,并根据dpi进行缩放 |
||||
* |
||||
* @param key 边距key |
||||
* @param defaultInsets 后备边距 |
||||
* @return 根据dpi缩放后的边距 |
||||
*/ |
||||
public static Insets getAndScaleUIInsets(String key, Insets defaultInsets) { |
||||
Insets margin = UIManager.getInsets(key); |
||||
Insets insets = (margin != null) ? margin : defaultInsets; |
||||
return FineUIScale.scale(insets); |
||||
} |
||||
|
||||
/** |
||||
* 绘制混合图像,含圆角、背景色设置 |
||||
* |
||||
* @param g 图像 |
||||
* @param composite 混合图像 |
||||
* @param background 背景色 |
||||
* @param width 宽度 |
||||
* @param height 高度 |
||||
* @param radius 圆角 |
||||
*/ |
||||
public static void paintWithComposite(Graphics g, Composite composite, Color background, |
||||
int width, int height, int radius) { |
||||
Graphics2D g2d = (Graphics2D) g; |
||||
|
||||
FlatUIUtils.setRenderingHints(g2d); |
||||
Composite oldComposite = g2d.getComposite(); |
||||
g2d.setComposite(composite); |
||||
|
||||
g2d.setColor(background); |
||||
g2d.fill(new RoundRectangle2D.Float(0, 0, width, height, radius, radius)); |
||||
g2d.setComposite(oldComposite); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 绘制部分圆角矩形边框 |
||||
* |
||||
* @param g2 Graphics2D |
||||
* @param x x坐标 |
||||
* @param y y坐标 |
||||
* @param width 宽度 |
||||
* @param height 高度 |
||||
* @param borderWidth 边框宽度 |
||||
* @param arc 圆角 |
||||
*/ |
||||
public static void paintPartRoundButtonBorder(Component c, Graphics2D g2, int x, int y, int width, int height, |
||||
float borderWidth, float arc) { |
||||
FlatUIUtils.setRenderingHints(g2); |
||||
arc = scale(arc); |
||||
float t = scale(borderWidth); |
||||
float t2x = t * 2; |
||||
Path2D path2D = new Path2D.Float(Path2D.WIND_EVEN_ODD); |
||||
if (isLeftRoundButton(c)) { |
||||
path2D.append(createLeftRoundRectangle(x, y, width, height, arc), false); |
||||
path2D.append(createLeftRoundRectangle(x + t, y + t, width - t, height - t2x, arc - t), false); |
||||
} else { |
||||
path2D.append(createRightRoundRectangle(x, y, width, height, arc), false); |
||||
path2D.append(createRightRoundRectangle(x, y + t, width - t, height - t2x, arc - t), false); |
||||
} |
||||
g2.fill(path2D); |
||||
} |
||||
|
||||
/** |
||||
* 绘制圆角tab边框 |
||||
* |
||||
* @param g2 Graphics2D |
||||
* @param x x坐标 |
||||
* @param y y坐标 |
||||
* @param width 宽度 |
||||
* @param height 高度 |
||||
* @param borderWidth 边框宽度 |
||||
* @param arc 圆角 |
||||
*/ |
||||
public static void paintRoundTabBorder(Graphics2D g2, double x, double y, double width, double height, |
||||
float borderWidth, float arc) { |
||||
FlatUIUtils.setRenderingHints(g2); |
||||
arc = scale(arc); |
||||
float t = scale(borderWidth); |
||||
float t2x = t * 2; |
||||
Path2D path2D = new Path2D.Float(Path2D.WIND_EVEN_ODD); |
||||
path2D.append(createTopRoundRectangle(x, y, width, height, arc), false); |
||||
path2D.append(createTopRoundRectangle(x + t, y + t, width - t2x, height - t, arc - t), false); |
||||
g2.fill(path2D); |
||||
} |
||||
|
||||
/** |
||||
* 创建一个部分圆角的矩形路径 |
||||
* |
||||
* @param x x坐标 |
||||
* @param y y坐标 |
||||
* @param width 矩形宽度 |
||||
* @param height 矩形高度 |
||||
* @param arcTopLeft 左上圆角弧度 |
||||
* @param arcTopRight 右上圆角弧度 |
||||
* @param arcBottomRight 右下圆角弧度 |
||||
* @param arcBottomLeft 左下圆角弧度 |
||||
* @return 路径 |
||||
*/ |
||||
public static Path2D createPartRoundRectangle(double x, double y, double width, double height, |
||||
double arcTopLeft, double arcTopRight, double arcBottomRight, double arcBottomLeft) { |
||||
Path2D path = new Path2D.Double(Path2D.WIND_EVEN_ODD, 7); |
||||
path.moveTo(x + arcTopLeft, y); |
||||
path.lineTo(x + width - arcTopRight, y); |
||||
path.quadTo(x + width, y, x + width, y + arcTopRight); |
||||
path.lineTo(x + width, y + height - arcBottomRight); |
||||
path.quadTo(x + width, y + height, x + width - arcBottomRight, y + height); |
||||
path.lineTo(x + arcBottomLeft, y + height); |
||||
path.quadTo(x, y + height, x, y + height - arcBottomLeft); |
||||
path.lineTo(x, y + arcTopLeft); |
||||
path.quadTo(x, y, x + arcTopLeft, y); |
||||
path.closePath(); |
||||
return path; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 创建一个左圆角矩形路径 |
||||
* |
||||
* @param x x坐标 |
||||
* @param y y坐标 |
||||
* @param width 矩形宽度 |
||||
* @param height 矩形高度 |
||||
* @param arc 圆角弧度 |
||||
* @return 路径 |
||||
*/ |
||||
public static Path2D createLeftRoundRectangle(float x, float y, float width, float height, float arc) { |
||||
return createPartRoundRectangle(x, y, width, height, arc, 0, 0, arc); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 创建一个右圆角矩形路径 |
||||
* |
||||
* @param x x坐标 |
||||
* @param y y坐标 |
||||
* @param width 矩形宽度 |
||||
* @param height 矩形高度 |
||||
* @param arc 圆角弧度 |
||||
* @return 路径 |
||||
*/ |
||||
public static Path2D createRightRoundRectangle(float x, float y, float width, float height, float arc) { |
||||
return createPartRoundRectangle(x, y, width, height, 0, arc, arc, 0); |
||||
} |
||||
|
||||
/** |
||||
* 创建一个顶圆角矩形路径 |
||||
* |
||||
* @param x x坐标 |
||||
* @param y y坐标 |
||||
* @param width 矩形宽度 |
||||
* @param height 矩形高度 |
||||
* @param arc 圆角弧度 |
||||
* @return 路径 |
||||
*/ |
||||
public static Path2D createTopRoundRectangle(double x, double y, double width, double height, double arc) { |
||||
return createPartRoundRectangle(x, y, width, height, arc, arc, 0, 0); |
||||
} |
||||
|
||||
/** |
||||
* 创建一个左上圆角的矩形路径 |
||||
* |
||||
* @param x x坐标 |
||||
* @param y y坐标 |
||||
* @param width 矩形宽度 |
||||
* @param height 矩形高度 |
||||
* @param arc 圆角弧度 |
||||
* @return 路径 |
||||
*/ |
||||
public static Path2D createTopLeftRoundRectangle(float x, float y, float width, float height, float arc) { |
||||
return createPartRoundRectangle(x, y, width, height, arc, 0, 0, 0); |
||||
} |
||||
|
||||
/** |
||||
* 创建一个左下圆角的矩形路径 |
||||
* |
||||
* @param x x坐标 |
||||
* @param y y坐标 |
||||
* @param width 矩形宽度 |
||||
* @param height 矩形高度 |
||||
* @param arc 圆角弧度 |
||||
* @return 路径 |
||||
*/ |
||||
public static Path2D createBottomLeftRoundRectangle(float x, float y, float width, float height, float arc) { |
||||
return createPartRoundRectangle(x, y, width, height, 0, 0, 0, arc); |
||||
} |
||||
|
||||
/** |
||||
* 创建一个右上圆角的矩形路径 |
||||
* |
||||
* @param x x坐标 |
||||
* @param y y坐标 |
||||
* @param width 矩形宽度 |
||||
* @param height 矩形高度 |
||||
* @param arc 圆角弧度 |
||||
* @return 路径 |
||||
*/ |
||||
public static Path2D createTopRightRoundRectangle(float x, float y, float width, float height, float arc) { |
||||
return createPartRoundRectangle(x, y, width, height, 0, arc, 0, 0); |
||||
} |
||||
|
||||
/** |
||||
* 创建一个右下圆角的矩形路径 |
||||
* |
||||
* @param x x坐标 |
||||
* @param y y坐标 |
||||
* @param width 矩形宽度 |
||||
* @param height 矩形高度 |
||||
* @param arc 圆角弧度 |
||||
* @return 路径 |
||||
*/ |
||||
public static Path2D createBottomRightRoundRectangle(float x, float y, float width, float height, float arc) { |
||||
return createPartRoundRectangle(x, y, width, height, 0, 0, arc, 0); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,140 @@
|
||||
package com.fr.design.data.datapane.management.search.pane; |
||||
|
||||
import com.fine.theme.icon.LazyIcon; |
||||
import com.fine.theme.light.ui.FineInputUI; |
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.fr.design.event.HoverAware; |
||||
import com.fr.design.gui.ibutton.UIButton; |
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.gui.itextfield.UITextField; |
||||
|
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.event.DocumentListener; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Insets; |
||||
import java.awt.event.ActionListener; |
||||
import java.awt.event.KeyListener; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
|
||||
/** |
||||
* 搜索面板 |
||||
* |
||||
* @author Leo.Qin |
||||
* @since 11.0 |
||||
* Created on 2023/12/13 |
||||
*/ |
||||
public class FineSearchPane extends JPanel implements HoverAware { |
||||
|
||||
private final Insets defaultLabelInsets = new Insets(3, 6, 3, 4); |
||||
private final Insets defaultButtonInsets = new Insets(4, 4, 4, 4); |
||||
private UITextField searchTextField; |
||||
private UIButton clearButton; |
||||
|
||||
|
||||
private static final String UI_CLASS_ID = "InputUI"; |
||||
|
||||
@Override |
||||
public String getUIClassID() { |
||||
return UI_CLASS_ID; |
||||
} |
||||
|
||||
private boolean hover; |
||||
|
||||
public FineSearchPane() { |
||||
this.setLayout(new BorderLayout()); |
||||
|
||||
initComponents(); |
||||
} |
||||
|
||||
private void initComponents() { |
||||
// 左侧搜索图标
|
||||
UILabel searchLabel = new UILabel(new LazyIcon("search")); |
||||
Insets labelInsets = FineUIUtils.getAndScaleUIInsets("SearchPanel.labelBorderInsets", defaultLabelInsets); |
||||
searchLabel.setBorder(BorderFactory.createEmptyBorder(labelInsets.top, labelInsets.left, labelInsets.bottom, labelInsets.right)); |
||||
|
||||
// 中间输入框
|
||||
searchTextField = new UITextField(); |
||||
searchTextField.setBorder(null); |
||||
searchTextField.setOpaque(false); |
||||
searchTextField.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseEntered(MouseEvent e) { |
||||
hover = true; |
||||
repaint(); |
||||
} |
||||
|
||||
@Override |
||||
public void mouseExited(MouseEvent e) { |
||||
hover = false; |
||||
repaint(); |
||||
} |
||||
}); |
||||
|
||||
// 右侧返回图标
|
||||
clearButton = new UIButton(new LazyIcon("clear")); |
||||
clearButton.setUI(new FineInputUI.FineInputButtonUI(false)); |
||||
Insets buttonInsets = FineUIUtils.getAndScaleUIInsets("SearchPanel.buttonBorderInsets", defaultButtonInsets); |
||||
clearButton.setBorder(BorderFactory.createEmptyBorder(buttonInsets.top, buttonInsets.left, buttonInsets.bottom, buttonInsets.right)); |
||||
|
||||
this.add(searchLabel, BorderLayout.WEST); |
||||
this.add(searchTextField, BorderLayout.CENTER); |
||||
this.add(clearButton, BorderLayout.EAST); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isHovered() { |
||||
return hover; |
||||
} |
||||
|
||||
/** |
||||
* 添加KeyListener |
||||
* |
||||
* @param listener the key listener. |
||||
*/ |
||||
public void addKeyListener(KeyListener listener) { |
||||
searchTextField.addKeyListener(listener); |
||||
} |
||||
|
||||
public void setPlaceholder(String placeHolder) { |
||||
searchTextField.setPlaceholder(placeHolder); |
||||
} |
||||
|
||||
public void setClearToolTipText(String text) { |
||||
clearButton.setToolTipText(text); |
||||
} |
||||
|
||||
/** |
||||
* 添加DocumentListener |
||||
* |
||||
* @param listener |
||||
*/ |
||||
public void addDocumentListener(DocumentListener listener) { |
||||
searchTextField.getDocument().addDocumentListener(listener); |
||||
} |
||||
|
||||
/** |
||||
* 按钮添加监听器 |
||||
* |
||||
* @param listener |
||||
*/ |
||||
public void addClearActionListener(ActionListener listener) { |
||||
clearButton.addActionListener(listener); |
||||
} |
||||
|
||||
public String getText() { |
||||
return searchTextField.getText(); |
||||
} |
||||
|
||||
public void setText(String text) { |
||||
searchTextField.setText(text); |
||||
} |
||||
|
||||
@Override |
||||
public void setEnabled(boolean enabled) { |
||||
super.setEnabled(enabled); |
||||
searchTextField.setEnabled(enabled); |
||||
clearButton.setEnabled(enabled); |
||||
} |
||||
} |
@ -0,0 +1,13 @@
|
||||
package com.fr.design.event; |
||||
|
||||
/** |
||||
* 组件判断是否被hover的能力接口 |
||||
* |
||||
* @author Levy.Xie |
||||
* @since 11.0 |
||||
* Created on 2023/12/07 |
||||
*/ |
||||
public interface HoverAware { |
||||
|
||||
boolean isHovered(); |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -1,173 +0,0 @@
|
||||
package com.fr.design.file; |
||||
|
||||
import com.fr.base.BaseUtils; |
||||
import com.fr.base.vcs.DesignerMode; |
||||
import com.fr.design.constants.UIConstants; |
||||
import com.fr.design.mainframe.DesignerContext; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
import java.awt.event.MouseEvent; |
||||
import java.awt.event.MouseListener; |
||||
import java.awt.event.MouseMotionListener; |
||||
import java.awt.geom.Line2D; |
||||
import java.awt.geom.Rectangle2D; |
||||
|
||||
/** |
||||
* Author : daisy |
||||
* Date: 13-8-27 |
||||
* Time: 下午6:07 |
||||
*/ |
||||
public abstract class NewTemplatePane extends JComponent implements MouseListener, MouseMotionListener { |
||||
|
||||
private static final Icon GRAY_NEW_CPT = BaseUtils.readIcon("/com/fr/design/images/buttonicon/additicon_grey.png"); |
||||
private static final int ICON_START_X = 5; |
||||
private static final int HEIGHT = 26; |
||||
private Graphics2D g2d; |
||||
private Icon newWorkBookIconMode = null; |
||||
|
||||
|
||||
public NewTemplatePane() { |
||||
newWorkBookIconMode = getNew(); |
||||
this.setLayout(new BorderLayout(0, 0)); |
||||
this.addMouseListener(this); |
||||
this.addMouseMotionListener(this); |
||||
this.setBorder(null); |
||||
this.setForeground(new Color(99, 99, 99)); |
||||
} |
||||
|
||||
public Dimension getPreferredSize() { |
||||
Dimension dim = super.getPreferredSize(); |
||||
dim.width = HEIGHT; |
||||
return dim; |
||||
} |
||||
|
||||
|
||||
public void paintComponent(Graphics g) { |
||||
super.paintComponent(g); |
||||
g2d = (Graphics2D) g; |
||||
g2d.setColor(UIConstants.TEMPLATE_TAB_PANE_BACKGROUND); |
||||
g2d.fill(new Rectangle2D.Double(0, 0, getWidth(),getHeight())); |
||||
int sheetIconY = (getHeight() - newWorkBookIconMode.getIconHeight()) / 2; |
||||
newWorkBookIconMode.paintIcon(this, g2d, ICON_START_X, sheetIconY); |
||||
// paintUnderLine(g2d);
|
||||
} |
||||
|
||||
|
||||
private void paintUnderLine(Graphics2D g2d) { |
||||
//画下面的那条线
|
||||
g2d.setPaint(UIConstants.LINE_COLOR); |
||||
g2d.draw(new Line2D.Double((float) 0, (float) (getHeight()-1), getWidth(), (float) (getHeight()-1))); |
||||
} |
||||
|
||||
/** |
||||
*鼠标点击 |
||||
* @param e 事件 |
||||
*/ |
||||
public void mouseClicked(MouseEvent e) { |
||||
if (needGrayNewCpt()) { |
||||
newWorkBookIconMode = GRAY_NEW_CPT; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
*鼠标按下 |
||||
* @param e 事件 |
||||
*/ |
||||
public void mousePressed(MouseEvent e) { |
||||
int evtX = e.getX(); |
||||
if (needGrayNewCpt()) { |
||||
newWorkBookIconMode = GRAY_NEW_CPT; |
||||
} |
||||
if (isOverNewIcon(evtX) && newWorkBookIconMode != GRAY_NEW_CPT) { |
||||
newWorkBookIconMode = getMousePressNew(); |
||||
createNewTemplate(); |
||||
} |
||||
this.repaint(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 新建模板 |
||||
*/ |
||||
protected void createNewTemplate() { |
||||
DesignerContext.getDesignerFrame().addAndActivateJTemplate(); |
||||
} |
||||
|
||||
/** |
||||
*鼠标松开 |
||||
* @param e 事件 |
||||
*/ |
||||
public void mouseReleased(MouseEvent e) { |
||||
if (needGrayNewCpt()) { |
||||
newWorkBookIconMode = GRAY_NEW_CPT; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
*鼠标进入 |
||||
* @param e 事件 |
||||
*/ |
||||
public void mouseEntered(MouseEvent e) { |
||||
if (needGrayNewCpt()) { |
||||
newWorkBookIconMode = GRAY_NEW_CPT; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
*鼠标离开 |
||||
* @param e 事件 |
||||
*/ |
||||
public void mouseExited(MouseEvent e) { |
||||
if (needGrayNewCpt()) { |
||||
newWorkBookIconMode = GRAY_NEW_CPT; |
||||
} else { |
||||
newWorkBookIconMode = getNew(); |
||||
} |
||||
|
||||
this.repaint(); |
||||
} |
||||
|
||||
/** |
||||
*鼠标拖拽 |
||||
* @param e 事件 |
||||
*/ |
||||
public void mouseDragged(MouseEvent e) { |
||||
} |
||||
|
||||
/** |
||||
*鼠标移动 |
||||
* @param e 事件 |
||||
*/ |
||||
public void mouseMoved(MouseEvent e) { |
||||
int evtX = e.getX(); |
||||
if (needGrayNewCpt()) { |
||||
newWorkBookIconMode = GRAY_NEW_CPT; |
||||
} else if (isOverNewIcon(evtX)) { |
||||
newWorkBookIconMode = getMouseOverNew(); |
||||
} |
||||
|
||||
this.repaint(); |
||||
|
||||
} |
||||
|
||||
private boolean needGrayNewCpt() { |
||||
return DesignerMode.isAuthorityEditing() || DesignerMode.isVcsMode(); |
||||
} |
||||
|
||||
|
||||
private boolean isOverNewIcon(int evtX) { |
||||
return (evtX >= ICON_START_X && evtX <= ICON_START_X + newWorkBookIconMode.getIconWidth()); |
||||
} |
||||
|
||||
public void setButtonGray(boolean isGray) { |
||||
newWorkBookIconMode = isGray ? GRAY_NEW_CPT : getNew(); |
||||
} |
||||
|
||||
public abstract Icon getNew(); |
||||
|
||||
public abstract Icon getMouseOverNew(); |
||||
|
||||
public abstract Icon getMousePressNew(); |
||||
|
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue