mirror of https://github.com/weisJ/darklaf.git
weisj
4 years ago
1 changed files with 0 additions and 544 deletions
@ -1,544 +0,0 @@ |
|||||||
/* |
|
||||||
* MIT License |
|
||||||
* |
|
||||||
* Copyright (c) 2020 Jannis Weis |
|
||||||
* |
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
||||||
* of this software and associated documentation files (the "Software"), to deal |
|
||||||
* in the Software without restriction, including without limitation the rights |
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
||||||
* copies of the Software, and to permit persons to whom the Software is |
|
||||||
* furnished to do so, subject to the following conditions: |
|
||||||
* |
|
||||||
* The above copyright notice and this permission notice shall be included in all |
|
||||||
* copies or substantial portions of the Software. |
|
||||||
* |
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
||||||
* SOFTWARE. |
|
||||||
* |
|
||||||
*/ |
|
||||||
import java.awt.*; |
|
||||||
import java.awt.event.*; |
|
||||||
import java.util.EventObject; |
|
||||||
|
|
||||||
import javax.swing.*; |
|
||||||
import javax.swing.border.Border; |
|
||||||
import javax.swing.event.CellEditorListener; |
|
||||||
import javax.swing.tree.*; |
|
||||||
|
|
||||||
public class Test { |
|
||||||
|
|
||||||
public static void main(final String[] args) { |
|
||||||
SwingUtilities.invokeLater(() -> { |
|
||||||
JFrame frame = new JFrame(); |
|
||||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
|
||||||
frame.setContentPane(createContentPane()); |
|
||||||
frame.pack(); |
|
||||||
frame.setLocationRelativeTo(null); |
|
||||||
frame.setVisible(true); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
private static JComponent createContentPane() { |
|
||||||
return new DemoContent(); |
|
||||||
} |
|
||||||
|
|
||||||
private static void drawRect(final Graphics g, final int x, final int y, final int w, final int h) { |
|
||||||
g.fillRect(x, y, w, 1); |
|
||||||
g.fillRect(x, y, 1, h); |
|
||||||
g.fillRect(x + w - 1, y, 1, h); |
|
||||||
g.fillRect(x, y + h - 1, w, 1); |
|
||||||
} |
|
||||||
|
|
||||||
private static class TreePopupManager { |
|
||||||
|
|
||||||
private static final TreePopupManager INSTANCE = new TreePopupManager(); |
|
||||||
|
|
||||||
private final TreePopupContent contentPanel = new TreePopupContent(this::pin); |
|
||||||
private Popup popup; |
|
||||||
private Component owner; |
|
||||||
private Point lastPos; |
|
||||||
private JDialog dialog; |
|
||||||
|
|
||||||
public TreePopupManager() { |
|
||||||
Toolkit.getDefaultToolkit().addAWTEventListener(e -> { |
|
||||||
if (popup != null) { |
|
||||||
if (e instanceof FocusEvent) { |
|
||||||
Component oppositeComp = ((FocusEvent) e).getOppositeComponent(); |
|
||||||
Component comp = ((FocusEvent) e).getComponent(); |
|
||||||
int id = e.getID(); |
|
||||||
if (id == FocusEvent.FOCUS_LOST) { |
|
||||||
if (oppositeComp == null |
|
||||||
|| !SwingUtilities.isDescendingFrom(oppositeComp, contentPanel)) { |
|
||||||
hidePopup(); |
|
||||||
} |
|
||||||
} else if (id == FocusEvent.FOCUS_GAINED) { |
|
||||||
if (comp == null |
|
||||||
|| !SwingUtilities.isDescendingFrom(comp, contentPanel)) { |
|
||||||
hidePopup(); |
|
||||||
} |
|
||||||
} |
|
||||||
} else if (e instanceof MouseEvent && e.getID() == MouseEvent.MOUSE_PRESSED) { |
|
||||||
Component component = ((MouseEvent) e).getComponent(); |
|
||||||
if (!SwingUtilities.isDescendingFrom(component, contentPanel)) { |
|
||||||
hidePopup(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}, AWTEvent.FOCUS_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK); |
|
||||||
} |
|
||||||
|
|
||||||
private void pin() { |
|
||||||
hidePopup(); |
|
||||||
if (dialog == null) { |
|
||||||
dialog = new JDialog(owner != null ? SwingUtilities.getWindowAncestor(owner) : null); |
|
||||||
dialog.setContentPane(contentPanel); |
|
||||||
contentPanel.setPinned(true); |
|
||||||
dialog.pack(); |
|
||||||
dialog.setLocation(lastPos); |
|
||||||
dialog.setVisible(true); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void hidePopup() { |
|
||||||
if (popup != null) { |
|
||||||
popup.hide(); |
|
||||||
popup = null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void hideDialog() { |
|
||||||
if (dialog != null) { |
|
||||||
dialog.setVisible(false); |
|
||||||
dialog.dispose(); |
|
||||||
dialog = null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public static TreePopupManager getInstance() { |
|
||||||
return INSTANCE; |
|
||||||
} |
|
||||||
|
|
||||||
public boolean isPopupVisible() { |
|
||||||
return popup != null || dialog != null; |
|
||||||
} |
|
||||||
|
|
||||||
public void showPopup(final Component parent, final JTree content, final Point p, final String title) { |
|
||||||
contentPanel.setTitle(title); |
|
||||||
contentPanel.setContent(content); |
|
||||||
if (dialog != null && !dialog.isVisible()) { |
|
||||||
hideDialog(); |
|
||||||
} |
|
||||||
if (popup == null && dialog == null) { |
|
||||||
contentPanel.setPinned(false); |
|
||||||
lastPos = p; |
|
||||||
owner = parent; |
|
||||||
popup = PopupFactory.getSharedInstance().getPopup(parent, contentPanel, p.x, p.y); |
|
||||||
popup.show(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static class TreePopupContent extends JPanel { |
|
||||||
|
|
||||||
private final JPanel view = new JPanel(new BorderLayout()); |
|
||||||
private final JLabel label = new JLabel(); |
|
||||||
private final JButton pinButton = new JButton("Pin"); |
|
||||||
|
|
||||||
public TreePopupContent(final Runnable onPin) { |
|
||||||
super(new BorderLayout()); |
|
||||||
JScrollPane scrollPane = new JScrollPane(view) { |
|
||||||
@Override |
|
||||||
public void setBorder(final Border border) {} |
|
||||||
}; |
|
||||||
add(scrollPane); |
|
||||||
Box box = Box.createHorizontalBox(); |
|
||||||
box.add(Box.createHorizontalStrut(5)); |
|
||||||
box.add(label); |
|
||||||
box.add(Box.createHorizontalGlue()); |
|
||||||
|
|
||||||
pinButton.setMargin(new Insets(0, 0, 0, 0)); |
|
||||||
pinButton.setFocusable(false); |
|
||||||
pinButton.setFocusPainted(false); |
|
||||||
pinButton.addActionListener(e -> onPin.run()); |
|
||||||
|
|
||||||
box.add(pinButton); |
|
||||||
box.add(Box.createHorizontalStrut(5)); |
|
||||||
add(box, BorderLayout.NORTH); |
|
||||||
} |
|
||||||
|
|
||||||
public void setPinned(final boolean pinned) { |
|
||||||
pinButton.setVisible(!pinned); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void updateUI() { |
|
||||||
super.updateUI(); |
|
||||||
setBorder(new JPopupMenu().getBorder()); |
|
||||||
} |
|
||||||
|
|
||||||
public void setTitle(final String title) { |
|
||||||
label.setText(title); |
|
||||||
} |
|
||||||
|
|
||||||
public void setContent(final JComponent content) { |
|
||||||
setBackground(content.getBackground()); |
|
||||||
view.removeAll(); |
|
||||||
view.add(content); |
|
||||||
revalidate(); |
|
||||||
repaint(); |
|
||||||
Dimension pref = view.getPreferredSize(); |
|
||||||
setPreferredSize(new Dimension(Math.max(pref.width, 100) + 10, |
|
||||||
Math.max(pref.height, 200) + 10)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static class DemoContent extends JPanel { |
|
||||||
|
|
||||||
private DemoContent() { |
|
||||||
setPreferredSize(new Dimension(300, 300)); |
|
||||||
setLayout(new BorderLayout()); |
|
||||||
setLayout(new GridLayout(2, 2)); |
|
||||||
add(new DemoContentPanel(new CheckBoxTree(populate(new TristateNode("root1"), 1, 1)))); |
|
||||||
add(new DemoContentPanel(new CheckBoxTree(populate(new TristateNode("root2"), 2, 2)))); |
|
||||||
add(new DemoContentPanel(new CheckBoxTree(populate(new TristateNode("root3"), 3, 3)))); |
|
||||||
add(new DemoContentPanel(new CheckBoxTree(populate(new TristateNode("root4"), 4, 4)))); |
|
||||||
} |
|
||||||
|
|
||||||
private TristateNode populate(final TristateNode node, final int count, final int depth) { |
|
||||||
if (depth == 0) return node; |
|
||||||
for (int i = 0; i < count; i++) { |
|
||||||
node.add(populate(new TristateNode("Node-" + depth + "-" + i), count, depth - 1)); |
|
||||||
} |
|
||||||
return node; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static class DemoContentPanel extends JPanel { |
|
||||||
|
|
||||||
private final CheckBoxTree tree; |
|
||||||
|
|
||||||
private DemoContentPanel(final CheckBoxTree tree) { |
|
||||||
this.tree = tree; |
|
||||||
setFocusable(true); |
|
||||||
addMouseListener(new MouseAdapter() { |
|
||||||
@Override |
|
||||||
public void mousePressed(final MouseEvent e) { |
|
||||||
requestFocus(); |
|
||||||
onMousePressed(e); |
|
||||||
} |
|
||||||
}); |
|
||||||
addFocusListener(new FocusListener() { |
|
||||||
@Override |
|
||||||
public void focusGained(final FocusEvent e) { |
|
||||||
repaint(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void focusLost(final FocusEvent e) { |
|
||||||
repaint(); |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void paintComponent(final Graphics g) { |
|
||||||
super.paintComponent(g); |
|
||||||
if (hasFocus()) { |
|
||||||
g.setColor(Color.RED); |
|
||||||
} else { |
|
||||||
g.setColor(Color.BLACK); |
|
||||||
} |
|
||||||
drawRect(g, 0, 0, getWidth(), getHeight()); |
|
||||||
} |
|
||||||
|
|
||||||
protected void onMousePressed(final MouseEvent e) { |
|
||||||
SwingUtilities.invokeLater(() -> { |
|
||||||
TreePopupManager manager = TreePopupManager.getInstance(); |
|
||||||
if (SwingUtilities.isRightMouseButton(e) || manager.isPopupVisible()) { |
|
||||||
Point p = e.getPoint(); |
|
||||||
SwingUtilities.convertPointToScreen(p, this); |
|
||||||
manager.showPopup(this, tree, p, "Tree"); |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static class CheckBoxTree extends JTree { |
|
||||||
|
|
||||||
public CheckBoxTree(final TristateNode root) { |
|
||||||
super(root); |
|
||||||
setShowsRootHandles(true); |
|
||||||
setCellRenderer(new CheckBoxTreeRenderer()); |
|
||||||
setCellEditor(new CheckBoxTreeEditor(this)); |
|
||||||
setEditable(true); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static class CheckBoxTreeRenderer extends DefaultTreeCellRenderer { |
|
||||||
|
|
||||||
private final TristateIcon icon = new TristateIcon(); |
|
||||||
|
|
||||||
@Override |
|
||||||
public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean sel, |
|
||||||
final boolean expanded, final boolean leaf, |
|
||||||
final int row, final boolean hasFocus) { |
|
||||||
Component component = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); |
|
||||||
JLabel label = (JLabel) component; // DefaultTreeCellRenderer uses JLabel as its renderer;
|
|
||||||
TristateNode node = (TristateNode) value; |
|
||||||
label.setText(node.getLabel()); |
|
||||||
label.setIcon(icon); |
|
||||||
icon.setState(node.getState()); |
|
||||||
return label; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static class CheckBoxTreeEditor implements TreeCellEditor { |
|
||||||
|
|
||||||
private final JTree tree; |
|
||||||
|
|
||||||
public CheckBoxTreeEditor(final JTree tree) { |
|
||||||
this.tree = tree; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Component getTreeCellEditorComponent(final JTree tree, final Object value, |
|
||||||
final boolean isSelected, |
|
||||||
final boolean expanded, |
|
||||||
final boolean leaf, final int row) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Object getCellEditorValue() { |
|
||||||
return ((TristateNode) tree.getEditingPath().getLastPathComponent()).getState(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean isCellEditable(final EventObject anEvent) { |
|
||||||
if (!(anEvent instanceof InputEvent)) return false; |
|
||||||
InputEvent event = (InputEvent) anEvent; |
|
||||||
Object source = event.getSource(); |
|
||||||
if (!(source instanceof JTree)) return false; |
|
||||||
JTree tree = (JTree) source; |
|
||||||
Object value = null; |
|
||||||
if (event instanceof MouseEvent) { |
|
||||||
Point p = ((MouseEvent) event).getPoint(); |
|
||||||
TreePath path = tree.getPathForLocation(p.x, p.y); |
|
||||||
if (path != null) { |
|
||||||
value = path.getLastPathComponent(); |
|
||||||
} |
|
||||||
} else { |
|
||||||
value = tree.getLeadSelectionPath().getLastPathComponent(); |
|
||||||
} |
|
||||||
if (value instanceof TristateNode) { |
|
||||||
((TristateNode) value).iterateState(); |
|
||||||
tree.repaint(); |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean shouldSelectCell(final EventObject anEvent) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean stopCellEditing() { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void cancelCellEditing() {} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void addCellEditorListener(final CellEditorListener l) {} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void removeCellEditorListener(final CellEditorListener l) {} |
|
||||||
} |
|
||||||
|
|
||||||
private static class TristateNode extends DefaultMutableTreeNode { |
|
||||||
private String label; |
|
||||||
|
|
||||||
public TristateNode() { |
|
||||||
this(null); |
|
||||||
} |
|
||||||
|
|
||||||
public TristateNode(final String label) { |
|
||||||
this(label, TristateState.DESELECTED); |
|
||||||
} |
|
||||||
|
|
||||||
public TristateNode(final String label, final TristateState state) { |
|
||||||
this(label, state, true); |
|
||||||
} |
|
||||||
|
|
||||||
public TristateNode(final String label, final TristateState state, final boolean allowsChildren) { |
|
||||||
super(); |
|
||||||
parent = null; |
|
||||||
this.allowsChildren = allowsChildren; |
|
||||||
this.userObject = state; |
|
||||||
this.label = label; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void add(final MutableTreeNode newChild) { |
|
||||||
if (!(newChild instanceof TristateNode)) { |
|
||||||
throw new IllegalArgumentException("Only children of type TristateTreeNode are allowed."); |
|
||||||
} |
|
||||||
super.add(newChild); |
|
||||||
} |
|
||||||
|
|
||||||
public TristateState getState() { |
|
||||||
return getUserObject(); |
|
||||||
} |
|
||||||
|
|
||||||
public void setState(final TristateState state) { |
|
||||||
setState(state, false, false); |
|
||||||
} |
|
||||||
|
|
||||||
private void setState(final TristateState state, final boolean invokedByParent, final boolean invokedByChild) { |
|
||||||
if (isLeaf() && state == TristateState.INDETERMINATE) { |
|
||||||
throw new IllegalArgumentException("Leaf nodes cannot have an indeterminate state"); |
|
||||||
} |
|
||||||
super.setUserObject(state); |
|
||||||
if (!isLeaf() && !invokedByChild) { |
|
||||||
if (state != TristateState.INDETERMINATE) { |
|
||||||
for (TreeNode node : children) { |
|
||||||
if (node instanceof TristateNode) { |
|
||||||
((TristateNode) node).setState(state, true, false); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
if (!invokedByParent) { |
|
||||||
Object treeNode = getParent(); |
|
||||||
if (treeNode instanceof TristateNode) { |
|
||||||
((TristateNode) treeNode).setState(((TristateNode) treeNode).getEffectiveState(), false, true); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void setSelected(final boolean selected) { |
|
||||||
this.userObject = selected; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public TristateState getUserObject() { |
|
||||||
return (TristateState) super.getUserObject(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setUserObject(final Object userObject) { |
|
||||||
if (!(userObject instanceof TristateState)) { |
|
||||||
throw new IllegalArgumentException("Only values of type TristateState are allowed but got " |
|
||||||
+ userObject); |
|
||||||
} |
|
||||||
setState((TristateState) userObject); |
|
||||||
} |
|
||||||
|
|
||||||
public TristateState getEffectiveState() { |
|
||||||
if (isLeaf()) return getState(); |
|
||||||
TristateState state = null; |
|
||||||
for (TreeNode node : children) { |
|
||||||
if (node instanceof TristateNode) { |
|
||||||
TristateState nodeState = ((TristateNode) node).getState(); |
|
||||||
if (state == null) state = nodeState; |
|
||||||
if (state != nodeState) { |
|
||||||
state = TristateState.INDETERMINATE; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return state != null ? state : TristateState.DESELECTED; |
|
||||||
} |
|
||||||
|
|
||||||
public String getLabel() { |
|
||||||
return label; |
|
||||||
} |
|
||||||
|
|
||||||
public void setLabel(final String label) { |
|
||||||
this.label = label; |
|
||||||
} |
|
||||||
|
|
||||||
public void iterateState() { |
|
||||||
TristateState state = getState(); |
|
||||||
switch (state) { |
|
||||||
case DESELECTED : |
|
||||||
setState(TristateState.SELECTED); |
|
||||||
break; |
|
||||||
case SELECTED : |
|
||||||
setState(TristateState.DESELECTED); |
|
||||||
break; |
|
||||||
case INDETERMINATE : |
|
||||||
setState(state.next()); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private enum TristateState { |
|
||||||
DESELECTED() { |
|
||||||
@Override |
|
||||||
public TristateState next() { |
|
||||||
return INDETERMINATE; |
|
||||||
} |
|
||||||
}, |
|
||||||
SELECTED() { |
|
||||||
@Override |
|
||||||
public TristateState next() { |
|
||||||
return DESELECTED; |
|
||||||
} |
|
||||||
}, |
|
||||||
INDETERMINATE() { |
|
||||||
@Override |
|
||||||
public TristateState next() { |
|
||||||
return SELECTED; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
public abstract TristateState next(); |
|
||||||
} |
|
||||||
|
|
||||||
private static class TristateIcon implements Icon { |
|
||||||
|
|
||||||
private TristateState state = TristateState.DESELECTED; |
|
||||||
|
|
||||||
@Override |
|
||||||
public void paintIcon(final Component c, final Graphics g, final int x, final int y) { |
|
||||||
switch (state) { |
|
||||||
case DESELECTED : |
|
||||||
g.setColor(Color.RED); |
|
||||||
break; |
|
||||||
case SELECTED : |
|
||||||
g.setColor(Color.GREEN); |
|
||||||
break; |
|
||||||
case INDETERMINATE : |
|
||||||
g.setColor(Color.ORANGE); |
|
||||||
break; |
|
||||||
default : |
|
||||||
return; |
|
||||||
} |
|
||||||
g.fillRect(x, y, getIconWidth(), getIconHeight()); |
|
||||||
} |
|
||||||
|
|
||||||
public void setState(final TristateState state) { |
|
||||||
this.state = state; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int getIconWidth() { |
|
||||||
return 16; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int getIconHeight() { |
|
||||||
return 16; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue