|
|
|
@ -3,17 +3,25 @@ package com.fr.design.gui.frpane;
|
|
|
|
|
import com.fine.swing.ui.layout.Row; |
|
|
|
|
import com.fine.theme.utils.FineUIScale; |
|
|
|
|
import com.fine.theme.utils.FineUIStyle; |
|
|
|
|
import com.fine.theme.utils.FineUIUtils; |
|
|
|
|
import com.finebi.cbb.utils.StringUtils; |
|
|
|
|
import com.fr.design.gui.ibutton.UIButton; |
|
|
|
|
import com.fr.design.gui.ilable.UILabel; |
|
|
|
|
import com.fr.file.FILE; |
|
|
|
|
import com.fr.general.ComparatorUtils; |
|
|
|
|
|
|
|
|
|
import javax.swing.BorderFactory; |
|
|
|
|
import java.awt.Color; |
|
|
|
|
import java.awt.Component; |
|
|
|
|
import java.awt.Dimension; |
|
|
|
|
import java.awt.Insets; |
|
|
|
|
import java.util.ArrayList; |
|
|
|
|
import java.util.List; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @author Renekton |
|
|
|
|
* @since 2024/08/08 |
|
|
|
|
* @param <T> |
|
|
|
|
*/ |
|
|
|
|
public class FineBreadcrumbPane <T> extends Row { |
|
|
|
|
|
|
|
|
|
private static final int SEP_HEIGHT = 20; |
|
|
|
@ -22,39 +30,119 @@ public class FineBreadcrumbPane <T> extends Row {
|
|
|
|
|
|
|
|
|
|
private static final int MAX_BUTTON_WIDTH = FineUIScale.scale(150); |
|
|
|
|
|
|
|
|
|
private static final String DEFAULT_SEP = "/"; |
|
|
|
|
|
|
|
|
|
private BreadcrumbButton currentCrumb; |
|
|
|
|
|
|
|
|
|
public void setRootButton(T text, final BreadcrumbBackListener<T> backListener) { |
|
|
|
|
clear(); |
|
|
|
|
currentCrumb = new BreadcrumbButton<>(getText(text), text); |
|
|
|
|
add(currentCrumb); |
|
|
|
|
final int index = getComponentCount(); |
|
|
|
|
addBreadcrumbListener(currentCrumb, index, backListener); |
|
|
|
|
repaint(); |
|
|
|
|
private List<Component> breadcrumbs; |
|
|
|
|
|
|
|
|
|
private int componentWidth; |
|
|
|
|
|
|
|
|
|
private int hidingIndex; |
|
|
|
|
|
|
|
|
|
private String sep; |
|
|
|
|
|
|
|
|
|
public FineBreadcrumbPane() { |
|
|
|
|
this(DEFAULT_SEP); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 可传入分隔符号 |
|
|
|
|
*/ |
|
|
|
|
public FineBreadcrumbPane(String sep) { |
|
|
|
|
this.sep = sep; |
|
|
|
|
this.breadcrumbs = new ArrayList<>(); |
|
|
|
|
this.componentWidth = 0; |
|
|
|
|
this.hidingIndex = -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void addCrumb(final T text, final BreadcrumbBackListener<T> backListener) { |
|
|
|
|
/** |
|
|
|
|
* 初始化节点 |
|
|
|
|
* @param data 节点数据 |
|
|
|
|
* @param backListener 节点点击关联动作 |
|
|
|
|
*/ |
|
|
|
|
public void init(T data, final BreadcrumbBackListener<T> backListener) { |
|
|
|
|
if (currentCrumb != null) { |
|
|
|
|
if (text instanceof FILE) { |
|
|
|
|
FILE file = (FILE) text; |
|
|
|
|
if (ComparatorUtils.equals(currentCrumb.getContent(), file) || !file.isDirectory()) { |
|
|
|
|
return; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
if (data instanceof FILE) { |
|
|
|
|
FILE file = (FILE) data; |
|
|
|
|
List<FILE> files = new ArrayList<>(); |
|
|
|
|
while (file != null) { |
|
|
|
|
|
|
|
|
|
if (StringUtils.isEmpty(file.getName())) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
if (file.getParent().equals(((FILE)currentCrumb.getContent()).getParent())) { |
|
|
|
|
remove(currentCrumb); |
|
|
|
|
currentCrumb = new BreadcrumbButton<>(getText(text), text); |
|
|
|
|
final int index = getComponentCount(); |
|
|
|
|
addBreadcrumbListener(currentCrumb, index, backListener); |
|
|
|
|
repaint(); |
|
|
|
|
return; |
|
|
|
|
if (file.isDirectory()) { |
|
|
|
|
files.add(file); |
|
|
|
|
} |
|
|
|
|
file = file.getParent(); |
|
|
|
|
} |
|
|
|
|
int size = files.size(); |
|
|
|
|
currentCrumb = new BreadcrumbButton<>(getRootdata((T) files.get(size-1)), files.get(size-1)); |
|
|
|
|
add(currentCrumb); |
|
|
|
|
final int index = getComponentCount(); |
|
|
|
|
addBreadcrumbListener(currentCrumb, index, backListener); |
|
|
|
|
for (int i = size-2; i >= 0; i--) { |
|
|
|
|
addCrumb((T) files.get(i), backListener); |
|
|
|
|
} |
|
|
|
|
} else if (data instanceof String) { |
|
|
|
|
currentCrumb = new BreadcrumbButton<>(getRootdata(data), data); |
|
|
|
|
add(currentCrumb); |
|
|
|
|
final int index = getComponentCount(); |
|
|
|
|
addBreadcrumbListener(currentCrumb, index, backListener); |
|
|
|
|
} |
|
|
|
|
repaint(); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 根节点重置 |
|
|
|
|
* @param data 节点内容 |
|
|
|
|
* @param backListener |
|
|
|
|
*/ |
|
|
|
|
public void reset(T data, final BreadcrumbBackListener<T> backListener) { |
|
|
|
|
if (currentCrumb != null && ComparatorUtils.equals(data, currentCrumb.getContent())) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
clear(); |
|
|
|
|
init(data, backListener); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 添加节点 |
|
|
|
|
* @param data |
|
|
|
|
* @param backListener |
|
|
|
|
*/ |
|
|
|
|
public void addCrumb(final T data, final BreadcrumbBackListener<T> backListener) { |
|
|
|
|
|
|
|
|
|
if (currentCrumb != null) { |
|
|
|
|
currentCrumb.setSelected(false); |
|
|
|
|
currentCrumb.setForeground(Color.GRAY); |
|
|
|
|
currentCrumb.setForeground(FineUIUtils.getUIColor("Button.breadcrumbForeground", "Button.breadcrumbForeground")); |
|
|
|
|
add(createSeparator()); |
|
|
|
|
} |
|
|
|
|
final BreadcrumbButton breadcrumb = createBreadcrumb(text); |
|
|
|
|
final BreadcrumbButton breadcrumb = createBreadcrumb(data); |
|
|
|
|
int currentWidth = componentWidth + breadcrumb.getPreferredSize().width; |
|
|
|
|
if (this.getVisibleRect().width != 0 && currentWidth > this.getVisibleRect().width) { |
|
|
|
|
// 大于可见宽度,隐藏前面的目录
|
|
|
|
|
for (int i = hidingIndex+1; i < breadcrumbs.size(); i++) { |
|
|
|
|
int width = breadcrumbs.get(i).getPreferredSize().width; |
|
|
|
|
breadcrumbs.get(i).setVisible(false); |
|
|
|
|
hidingIndex = i; |
|
|
|
|
currentWidth -= width; |
|
|
|
|
if (currentWidth < this.getVisibleRect().width) { |
|
|
|
|
if (breadcrumbs.get(i+1) instanceof UILabel) { |
|
|
|
|
breadcrumbs.get(i+1).setVisible(false); |
|
|
|
|
hidingIndex = i+1; |
|
|
|
|
componentWidth = currentWidth - breadcrumb.getPreferredSize().width - breadcrumbs.get(i+1).getPreferredSize().width; |
|
|
|
|
} else { |
|
|
|
|
componentWidth = currentWidth - breadcrumb.getPreferredSize().width; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
add(breadcrumb); |
|
|
|
|
currentCrumb = breadcrumb; |
|
|
|
|
final int index = getComponentCount(); |
|
|
|
@ -71,13 +159,30 @@ public class FineBreadcrumbPane <T> extends Row {
|
|
|
|
|
repaint(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void remove(Component comp) { |
|
|
|
|
componentWidth -= comp.getPreferredSize().getWidth(); |
|
|
|
|
breadcrumbs.remove(comp); |
|
|
|
|
super.remove(comp); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public Component add(Component comp) { |
|
|
|
|
componentWidth += comp.getPreferredSize().getWidth(); |
|
|
|
|
breadcrumbs.add(comp); |
|
|
|
|
return super.add(comp); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void clear() { |
|
|
|
|
|
|
|
|
|
removeAll(); |
|
|
|
|
currentCrumb = null; |
|
|
|
|
componentWidth = 0; |
|
|
|
|
breadcrumbs.clear(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected Component createSeparator() { |
|
|
|
|
UILabel separator = new UILabel("/"); |
|
|
|
|
UILabel separator = new UILabel(sep); |
|
|
|
|
separator.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2)); |
|
|
|
|
separator.setPreferredSize(new Dimension(separator.getPreferredSize().width, FineUIScale.scale(SEP_HEIGHT))); |
|
|
|
|
|
|
|
|
@ -85,18 +190,48 @@ public class FineBreadcrumbPane <T> extends Row {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected BreadcrumbButton createBreadcrumb(T content) { |
|
|
|
|
BreadcrumbButton button = new BreadcrumbButton(getText(content), content); |
|
|
|
|
BreadcrumbButton button = new BreadcrumbButton(getdata(content), content); |
|
|
|
|
button.setPreferredSize(new Dimension(Math.min(button.getPreferredSize().width, MAX_BUTTON_WIDTH), FineUIScale.scale(BUTTON_HEIGHT))); |
|
|
|
|
button.setSelected(true); |
|
|
|
|
button.setForeground(Color.black); |
|
|
|
|
button.setForeground(FineUIUtils.getUIColor("Button.breadcrumbSelectedForeground", "Button.breadcrumbSelectedForeground")); |
|
|
|
|
return button; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 节点点击事件 |
|
|
|
|
* @param breadcrumb |
|
|
|
|
* @param index |
|
|
|
|
* @param backListener |
|
|
|
|
*/ |
|
|
|
|
private void addBreadcrumbListener(BreadcrumbButton breadcrumb, int index, BreadcrumbBackListener backListener) { |
|
|
|
|
breadcrumb.addActionListener(e -> { |
|
|
|
|
removeTailCrumbs(index); |
|
|
|
|
// 存在隐藏目录,弹出部分隐藏目录
|
|
|
|
|
if (hidingIndex >= 0) { |
|
|
|
|
int currentWidth = componentWidth; |
|
|
|
|
for (int i = hidingIndex; i >= 0; i--) { |
|
|
|
|
currentWidth += breadcrumbs.get(i).getPreferredSize().width; |
|
|
|
|
if (currentWidth <= this.getVisibleRect().width) { |
|
|
|
|
breadcrumbs.get(i).setVisible(true); |
|
|
|
|
} else { |
|
|
|
|
hidingIndex = i; |
|
|
|
|
if (breadcrumbs.get(i+1) instanceof UILabel) { |
|
|
|
|
currentWidth -= breadcrumbs.get(i+1).getPreferredSize().width; |
|
|
|
|
breadcrumbs.get(i+1).setVisible(false); |
|
|
|
|
hidingIndex = i+1; |
|
|
|
|
} |
|
|
|
|
componentWidth = currentWidth - breadcrumbs.get(i).getPreferredSize().width; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (currentWidth <= this.getVisibleRect().width) { |
|
|
|
|
hidingIndex = -1; |
|
|
|
|
componentWidth = currentWidth; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
breadcrumb.setSelected(true); |
|
|
|
|
breadcrumb.setForeground(Color.BLACK); |
|
|
|
|
breadcrumb.setForeground(FineUIUtils.getUIColor("Button.breadcrumbSelectedForeground", "Button.breadcrumbSelectedForeground")); |
|
|
|
|
if (breadcrumb != currentCrumb) { |
|
|
|
|
currentCrumb = breadcrumb; |
|
|
|
|
} |
|
|
|
@ -106,21 +241,36 @@ public class FineBreadcrumbPane <T> extends Row {
|
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private String getText(T text) { |
|
|
|
|
if (text instanceof String) { |
|
|
|
|
return (String) text; |
|
|
|
|
} else if (text instanceof FILE){ |
|
|
|
|
return ((FILE)text).getName(); |
|
|
|
|
private String getRootdata(T data) { |
|
|
|
|
if (data instanceof String) { |
|
|
|
|
return (String) data; |
|
|
|
|
} else if (data instanceof FILE){ |
|
|
|
|
FILE file = (FILE) data; |
|
|
|
|
if (file.getPath().endsWith("/") || file.getPath().endsWith("\\")) { |
|
|
|
|
return file.getPath().replaceAll("[/\\\\]+", ""); |
|
|
|
|
} else if (file.getPath().contains("/") || file.getPath().contains("\\")) { |
|
|
|
|
return file.getPath().replaceAll("[/\\\\]+", sep); |
|
|
|
|
} |
|
|
|
|
return ((FILE)data).getPath(); |
|
|
|
|
} |
|
|
|
|
return (String) text; |
|
|
|
|
return (String) data; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private String getdata(T data) { |
|
|
|
|
if (data instanceof String) { |
|
|
|
|
return (String) data; |
|
|
|
|
} else if (data instanceof FILE){ |
|
|
|
|
return ((FILE)data).getName(); |
|
|
|
|
} |
|
|
|
|
return (String) data; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static class BreadcrumbButton <T> extends UIButton { |
|
|
|
|
|
|
|
|
|
private T content; |
|
|
|
|
|
|
|
|
|
public BreadcrumbButton(String text, T content) { |
|
|
|
|
super(text); |
|
|
|
|
public BreadcrumbButton(String data, T content) { |
|
|
|
|
super(data); |
|
|
|
|
BreadcrumbButton.this.content = content; |
|
|
|
|
setMargin(new Insets(0, 0, 0, 0)); |
|
|
|
|
setBorder(BorderFactory.createEmptyBorder()); |
|
|
|
@ -148,6 +298,7 @@ public class FineBreadcrumbPane <T> extends Row {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public interface BreadcrumbBackListener <T> { |
|
|
|
|
void onBreadcrumbBack(T text); |
|
|
|
|
void onBreadcrumbBack(T data); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|