mirror of https://github.com/weisJ/darklaf.git
weisj
5 years ago
22 changed files with 3904 additions and 829 deletions
@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<module version="4"> |
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_12_PREVIEW"> |
||||
<output url="file://$MODULE_DIR$/target/classes" /> |
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" /> |
||||
<content url="file://$MODULE_DIR$"> |
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> |
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> |
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> |
||||
<excludeFolder url="file://$MODULE_DIR$/target" /> |
||||
</content> |
||||
<orderEntry type="inheritedJdk" /> |
||||
<orderEntry type="sourceFolder" forTests="false" /> |
||||
<orderEntry type="library" name="Maven: darcula:darcula.laf:1.0.0" level="project" /> |
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.swinglabs:swingx:1.6.1" level="project" /> |
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.jhlabs:filters:2.0.235" level="project" /> |
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.swinglabs:swing-worker:1.1" level="project" /> |
||||
<orderEntry type="library" name="Maven: com.kitfox.svg:svg-salamander:1.0" level="project" /> |
||||
<orderEntry type="library" name="Maven: org.jetbrains:annotations:16.0.1" level="project" /> |
||||
</component> |
||||
</module> |
@ -1,26 +1,176 @@
|
||||
package com.weis.darklaf.ui.button; |
||||
|
||||
import com.weis.darklaf.util.DarkUIUtil; |
||||
import com.weis.darklaf.util.GraphicsContext; |
||||
import com.weis.darklaf.util.GraphicsUtil; |
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
import sun.swing.SwingUtilities2; |
||||
|
||||
import javax.swing.*; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.*; |
||||
import java.awt.geom.Ellipse2D; |
||||
import java.awt.geom.RoundRectangle2D; |
||||
import java.beans.PropertyChangeListener; |
||||
|
||||
public class DarkToggleButtonUI extends DarkButtonUI { |
||||
|
||||
private static final int SLIDER_HEIGHT = 17; |
||||
private static final int SLIDER_WIDTH = 35; |
||||
private static Rectangle rect = new Rectangle(); |
||||
|
||||
private final PropertyChangeListener propertyChangeListener = evt -> { |
||||
if ("ToggleButton.variant".equals(evt.getPropertyName())) { |
||||
var oldVal = evt.getOldValue(); |
||||
var newVal = evt.getNewValue(); |
||||
if (oldVal != null && oldVal.equals(newVal)) { |
||||
return; |
||||
} |
||||
if ("slider".equals(newVal)) { |
||||
button.setBorderPainted(false); |
||||
} else { |
||||
button.setBorderPainted(true); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
@NotNull |
||||
@Contract(value = "_ -> new", pure = true) |
||||
public static ComponentUI createUI(final JComponent c) { |
||||
return new DarkToggleButtonUI(); |
||||
} |
||||
|
||||
@Override |
||||
protected void installListeners(final AbstractButton b) { |
||||
super.installListeners(b); |
||||
button.addPropertyChangeListener(propertyChangeListener); |
||||
} |
||||
|
||||
@Override |
||||
protected void uninstallListeners(final AbstractButton b) { |
||||
super.uninstallListeners(b); |
||||
button.removePropertyChangeListener(propertyChangeListener); |
||||
} |
||||
|
||||
@Override |
||||
public boolean contains(@NotNull final JComponent c, final int x, final int y) { |
||||
if (!(x >= 0 && x <= c.getWidth() && y >= 0 && y <= c.getHeight())) return false; |
||||
var bounds = getSliderBounds(c); |
||||
return new RoundRectangle2D.Float(bounds.x, bounds.y, bounds.width, bounds.height, |
||||
bounds.height, bounds.height).contains(x,y); |
||||
} |
||||
|
||||
@Override |
||||
public void paint(final Graphics g, @NotNull final JComponent c) { |
||||
if (isSlider(c)) { |
||||
GraphicsContext config = GraphicsUtil.setupStrokePainting(g); |
||||
AbstractButton b = (AbstractButton) c; |
||||
String text = layout(b, c, SwingUtilities2.getFontMetrics(b, g), |
||||
b.getWidth(), b.getHeight()); |
||||
|
||||
paintSlider((Graphics2D) g, b); |
||||
paintIcon(g, b, c); |
||||
paintText(g, b, c, text); |
||||
config.restore(); |
||||
} else { |
||||
super.paint(g, c); |
||||
} |
||||
} |
||||
|
||||
private void paintSlider(@NotNull final Graphics2D g, final AbstractButton c) { |
||||
var bounds = getSliderBounds(c); |
||||
g.translate(bounds.x, bounds.y); |
||||
Shape slider = new RoundRectangle2D.Float(0, 0, bounds.width, bounds.height, |
||||
bounds.height, bounds.height); |
||||
g.setColor(getBackgroundColor(c)); |
||||
g.fill(slider); |
||||
g.setColor(getToggleBorderColor(c)); |
||||
g.draw(slider); |
||||
if (c.hasFocus()) { |
||||
var config = new GraphicsContext(g); |
||||
g.setComposite(DarkUIUtil.ALPHA_COMPOSITE); |
||||
g.translate(-2, -2); |
||||
DarkUIUtil.paintFocusBorder(g, bounds.width + 4, bounds.height + 4, |
||||
(float) (bounds.height / 2.0) + 2, true); |
||||
g.translate(2, 2); |
||||
config.restore(); |
||||
} |
||||
g.setColor(getSliderColor(c)); |
||||
if (c.isSelected()) { |
||||
g.fill(new Ellipse2D.Float( |
||||
bounds.width - bounds.height + 1, 1, bounds.height - 1.5f, bounds.height - 1.5f)); |
||||
} else { |
||||
g.fill(new Ellipse2D.Float(1, 1, bounds.height - 1.5f, bounds.height - 1.5f)); |
||||
} |
||||
g.translate(-bounds.x, -bounds.y); |
||||
} |
||||
|
||||
private static Color getSliderColor(@NotNull final AbstractButton b) { |
||||
return b.isEnabled() ? UIManager.getColor("ToggleButton.sliderColor") |
||||
: UIManager.getColor("ToggleButton.disabledSliderColor"); |
||||
} |
||||
|
||||
private static Color getToggleBorderColor(@NotNull final AbstractButton b) { |
||||
return b.isEnabled() ? UIManager.getColor("ToggleButton.sliderBorderColor") |
||||
: UIManager.getColor("ToggleButton.disabledSliderBorderColor"); |
||||
} |
||||
|
||||
|
||||
public Dimension getPreferredSize(final JComponent c) { |
||||
Dimension d = super.getPreferredSize(c); |
||||
d.width += SLIDER_WIDTH + DarkButtonBorder.BORDER_SIZE; |
||||
return d; |
||||
} |
||||
|
||||
@Override |
||||
protected String layout(@NotNull final AbstractButton b, final JComponent c, |
||||
final FontMetrics fm, final int width, final int height) { |
||||
if (isSlider(c)) { |
||||
Insets i = b.getInsets(); |
||||
var bounds = getSliderBounds(c); |
||||
viewRect.x = bounds.x + bounds.width + DarkButtonBorder.BORDER_SIZE; |
||||
viewRect.y = i.top; |
||||
viewRect.width = width - (i.right + viewRect.x); |
||||
viewRect.height = height - (i.bottom + viewRect.y); |
||||
|
||||
textRect.x = textRect.y = textRect.width = textRect.height = 0; |
||||
iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0; |
||||
|
||||
// layout the text and icon
|
||||
return SwingUtilities.layoutCompoundLabel( |
||||
b, fm, b.getText(), b.getIcon(), |
||||
b.getVerticalAlignment(), b.getHorizontalAlignment(), |
||||
b.getVerticalTextPosition(), b.getHorizontalTextPosition(), |
||||
viewRect, iconRect, textRect, |
||||
b.getText() == null ? 0 : b.getIconTextGap()); |
||||
} else { |
||||
return super.layout(b, c, fm, width, height); |
||||
} |
||||
} |
||||
|
||||
@NotNull |
||||
private Rectangle getSliderBounds(@NotNull final JComponent c) { |
||||
int x = DarkButtonBorder.BORDER_SIZE; |
||||
int y = (c.getHeight() - SLIDER_HEIGHT) / 2; |
||||
rect.x = x; |
||||
rect.y = y; |
||||
rect.width = SLIDER_WIDTH; |
||||
rect.height = SLIDER_HEIGHT; |
||||
return rect; |
||||
} |
||||
|
||||
@Contract("null -> false") |
||||
private static boolean isSlider(final JComponent c) { |
||||
return c instanceof JToggleButton |
||||
&& "slider".equals(c.getClientProperty("ToggleButton.variant")); |
||||
} |
||||
|
||||
protected Color getBackgroundColor(@NotNull final JComponent c) { |
||||
return c.isEnabled() || (c instanceof JToggleButton && ((JToggleButton) c).isSelected()) |
||||
? (c instanceof JButton && (((JButton) c).isDefaultButton())) |
||||
? UIManager.getColor("Button.darcula.defaultFillColor") |
||||
: UIManager.getColor("Button.darcula.activeFillColor") |
||||
: UIManager.getColor("Button.darcula.inactiveFillColor"); |
||||
if ((c instanceof JToggleButton && ((JToggleButton) c).isSelected())) { |
||||
return UIManager.getColor("Button.darcula.activeFillColor"); |
||||
} else { |
||||
return UIManager.getColor("Button.darcula.inactiveFillColor"); |
||||
} |
||||
} |
||||
} |
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,655 @@
|
||||
package com.weis.darklaf.ui.tabbedpane; |
||||
|
||||
import com.weis.darklaf.util.ImageUtil; |
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
import java.awt.datatransfer.DataFlavor; |
||||
import java.awt.datatransfer.Transferable; |
||||
import java.awt.datatransfer.UnsupportedFlavorException; |
||||
import java.awt.dnd.DragGestureEvent; |
||||
import java.awt.dnd.DragGestureListener; |
||||
import java.awt.dnd.DragGestureRecognizer; |
||||
import java.awt.dnd.DragSource; |
||||
import java.awt.dnd.DragSourceContext; |
||||
import java.awt.dnd.DragSourceDragEvent; |
||||
import java.awt.dnd.DragSourceDropEvent; |
||||
import java.awt.dnd.DragSourceEvent; |
||||
import java.awt.dnd.DragSourceListener; |
||||
import java.awt.dnd.DropTargetDragEvent; |
||||
import java.awt.dnd.DropTargetDropEvent; |
||||
import java.awt.dnd.DropTargetEvent; |
||||
import java.awt.dnd.DropTargetListener; |
||||
import java.awt.event.InputEvent; |
||||
import java.awt.event.MouseEvent; |
||||
|
||||
|
||||
public class TabbedPaneTransferHandler extends TransferHandler implements DropTargetListener, SwingConstants { |
||||
|
||||
private static final String MIME_TYPE = DataFlavor.javaJVMLocalObjectMimeType + ";class=javax.swing.JTabbedPane"; |
||||
private static final Rectangle EMPTY_RECT = new Rectangle(0, 0, 0, 0); |
||||
private static TabbedPaneDragGestureRecognizer recognizer = null; |
||||
|
||||
private int lastTab = -1; |
||||
private DataFlavor tabFlavor; |
||||
|
||||
/** |
||||
* The location of the mouse cursor throughout the drag-and-drop. |
||||
* This is here because of a deficiency in TransferHandler's design; you |
||||
* have no way of knowing the exact drop location in the component with a |
||||
* plain TransferHandler unless you implement DropTargetListener and get |
||||
* it that way. |
||||
*/ |
||||
protected Point mouseLocation; |
||||
|
||||
private TabTransferable currentTransferable; |
||||
|
||||
|
||||
public TabbedPaneTransferHandler() { |
||||
try { |
||||
tabFlavor = new DataFlavor(MIME_TYPE); |
||||
} catch (ClassNotFoundException ignored) { |
||||
} |
||||
} |
||||
|
||||
public void exportAsDrag(final JComponent comp, final InputEvent e, final int a) { |
||||
int srcActions = getSourceActions(comp); |
||||
int action = a; |
||||
|
||||
// only mouse events supported for drag operations
|
||||
if (!(e instanceof MouseEvent) |
||||
// only support known actions
|
||||
|| !(action == COPY || action == MOVE || action == LINK) |
||||
// only support valid source actions
|
||||
|| (srcActions & action) == 0) { |
||||
|
||||
action = NONE; |
||||
} |
||||
|
||||
if (action != NONE && !GraphicsEnvironment.isHeadless()) { |
||||
if (recognizer == null) { |
||||
recognizer = new TabbedPaneDragGestureRecognizer(new TabbedPaneDragHandler()); |
||||
} |
||||
recognizer.gestured(comp, (MouseEvent) e, srcActions, action); |
||||
} else { |
||||
exportDone(comp, null, NONE); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Overridden to include a check for a TabData flavor. |
||||
*/ |
||||
@Override |
||||
public boolean canImport(final JComponent c, final DataFlavor[] flavors) { |
||||
return hasTabFlavor(flavors); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected Transferable createTransferable(final JComponent c) { |
||||
JTabbedPane tabPane = (JTabbedPane) c; |
||||
currentTransferable = new TabTransferable(tabPane); |
||||
var ui = supportsIndicator(c); |
||||
int index = currentTransferable.transferData.tabIndex; |
||||
if (tabPane.getTabCount() > 1) { |
||||
if (index == 0) { |
||||
index++; |
||||
} else { |
||||
index--; |
||||
} |
||||
} else { |
||||
index = -1; |
||||
} |
||||
tabPane.setSelectedIndex(index); |
||||
if (ui != null) { |
||||
ui.setRolloverTab(-1); |
||||
createDragImage(tabPane, ui); |
||||
ui.setSourceIndicator(currentTransferable.transferData.tabIndex); |
||||
} else { |
||||
createDragImage(tabPane, null); |
||||
} |
||||
if ((ui != null && !ui.scrollableTabLayoutEnabled()) |
||||
|| tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) { |
||||
tabPane.setSelectedIndex(currentTransferable.transferData.tabIndex); |
||||
} |
||||
return currentTransferable; |
||||
} |
||||
|
||||
protected void createDragImage(@NotNull final JTabbedPane tabbedPane, final DarkTabbedPaneUI ui) { |
||||
Image tabImage = ImageUtil.imageFromComponent(tabbedPane, currentTransferable.transferData.tabBounds); |
||||
int w = tabImage.getWidth(null); |
||||
int h = tabImage.getHeight(null); |
||||
var g = tabImage.getGraphics(); |
||||
|
||||
if (ui != null) { |
||||
g.setColor(ui.getDragBorderColor()); |
||||
} else { |
||||
g.setColor(tabbedPane.getBackgroundAt(currentTransferable.transferData.tabIndex).brighter()); |
||||
} |
||||
|
||||
int lw = 2; |
||||
g.fillRect(0, 0, w, lw); |
||||
g.fillRect(0, 0, lw, h); |
||||
g.fillRect(w - lw, 0, lw, h); |
||||
g.fillRect(0, h - lw, w, lw); |
||||
g.dispose(); |
||||
|
||||
setDragImageOffset(new Point(w / 2, h / 2)); |
||||
setDragImage(tabImage); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void dragEnter(final DropTargetDragEvent e) { |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void dragExit(@NotNull final DropTargetEvent e) { |
||||
Component c = e.getDropTargetContext().getComponent(); |
||||
lastTab = -1; |
||||
var ui = supportsIndicator(c); |
||||
if (ui != null) { |
||||
ui.clearDropIndicator(); |
||||
} |
||||
} |
||||
|
||||
@Contract("null -> null") |
||||
private DarkTabbedPaneUI supportsIndicator(final Component c) { |
||||
if (c instanceof JComponent && ((JComponent) c).getUI() instanceof DarkTabbedPaneUI) { |
||||
return ((DarkTabbedPaneUI) ((JComponent) c).getUI()); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public void dragOver(@NotNull final DropTargetDragEvent e) { |
||||
e.getDropTargetContext().getComponent().setCursor(Cursor.getDefaultCursor()); |
||||
mouseLocation = e.getLocation(); |
||||
|
||||
Component c = e.getDropTargetContext().getComponent(); |
||||
JTabbedPane destTabbedPane = (JTabbedPane) c; |
||||
|
||||
var ui = supportsIndicator(destTabbedPane); |
||||
if (ui != null) { |
||||
TabTransferable t = currentTransferable; |
||||
if (t != null) { |
||||
int tab = getDroppedTabIndex(t, destTabbedPane, mouseLocation); |
||||
|
||||
if (tab == -1) { |
||||
lastTab = tab; |
||||
ui.clearDropIndicator(); |
||||
return; |
||||
} |
||||
int tabPlacement = destTabbedPane.getTabPlacement(); |
||||
Rectangle dropRect = t.getTabBounds(); |
||||
Rectangle destRect = destTabbedPane.getBoundsAt(Math.min(tab, destTabbedPane.getTabCount() - 1)); |
||||
JTabbedPane source = t.transferData.sourceTabbedPane; |
||||
int sourceIndex = t.transferData.tabIndex; |
||||
|
||||
|
||||
if (ui.scrollableTabLayoutEnabled()) { |
||||
boolean lastInSource = false; |
||||
if (destTabbedPane == source && (tab == sourceIndex || (sourceIndex == source.getTabCount() - 1 |
||||
&& tab == source.getTabCount()))) { |
||||
lastInSource = true; |
||||
destRect.width = dropRect.width; |
||||
destRect.height = dropRect.height; |
||||
} |
||||
|
||||
switch (tabPlacement) { |
||||
case TOP, BOTTOM -> { |
||||
if (destTabbedPane.getComponentOrientation().isLeftToRight()) { |
||||
if (tab >= destTabbedPane.getTabCount() && !lastInSource) { |
||||
destRect.x += destRect.width; |
||||
} |
||||
dropRect.x = destRect.x; |
||||
if (lastTab != -1 && lastTab < tab) { |
||||
dropRect.x -= dropRect.width; |
||||
} |
||||
} else { |
||||
if (tab >= destTabbedPane.getTabCount()) { |
||||
destRect.x -= 2 * dropRect.width; |
||||
if (lastTab < tab) { |
||||
destRect.x += destRect.width; |
||||
} |
||||
} |
||||
dropRect.x = destRect.x + dropRect.width; |
||||
if (lastTab != -1 && lastTab > tab) { |
||||
dropRect.x -= dropRect.width; |
||||
} |
||||
} |
||||
dropRect.y = destRect.y + destRect.height - dropRect.height; |
||||
} |
||||
case LEFT, RIGHT -> { |
||||
if (tab >= destTabbedPane.getTabCount()) { |
||||
destRect.y += destRect.height; |
||||
} |
||||
dropRect.y = destRect.y; |
||||
dropRect.x = destRect.x + destRect.width - dropRect.width; |
||||
if (lastTab != -1 && lastTab < tab) { |
||||
dropRect.y -= dropRect.height; |
||||
} |
||||
} |
||||
} |
||||
} else { |
||||
if (source == destTabbedPane && (tab == sourceIndex || tab == sourceIndex + 1)) { |
||||
dropRect.setRect(0, 0, 0, 0); |
||||
} else { |
||||
switch (destTabbedPane.getTabPlacement()) { |
||||
case TOP, BOTTOM -> { |
||||
var b = ui.getTabAreaBounds(); |
||||
if (tab == destTabbedPane.getTabCount()) { |
||||
dropRect.x = destRect.x + destRect.width / 2; |
||||
dropRect.width = Math.min(b.x + b.width - dropRect.x, dropRect.width); |
||||
} else if (tab == 0) { |
||||
dropRect.x = destRect.x; |
||||
dropRect.width = Math.min(dropRect.width / 2, destRect.width / 2); |
||||
} else { |
||||
var prev = destTabbedPane.getBoundsAt(tab - 1); |
||||
if (destRect.y + destRect.height <= mouseLocation.y && |
||||
prev.y <= mouseLocation.y && mouseLocation.y <= prev.y + prev.height) { |
||||
destRect.x = prev.x + prev.width; |
||||
destRect.y = prev.y; |
||||
destRect.height = prev.height; |
||||
} |
||||
|
||||
dropRect.x = destRect.x; |
||||
dropRect.setLocation(destRect.getLocation()); |
||||
dropRect.x -= dropRect.width / 2; |
||||
if (dropRect.x < b.x) { |
||||
int diff = b.x - dropRect.x; |
||||
dropRect.width -= diff; |
||||
dropRect.x = b.x; |
||||
} |
||||
if (dropRect.x + dropRect.width > b.x + b.width) { |
||||
int diff = dropRect.width + dropRect.x - b.width - b.x; |
||||
dropRect.width -= diff; |
||||
} |
||||
} |
||||
dropRect.y = destRect.y + destRect.height - dropRect.height; |
||||
} |
||||
case LEFT, RIGHT -> { |
||||
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
ui.setDnDIndicatorRect(dropRect.x, dropRect.y, dropRect.width, dropRect.height, |
||||
tab, source == destTabbedPane); |
||||
lastTab = tab; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void drop(@NotNull final DropTargetDropEvent e) { |
||||
Component c = e.getDropTargetContext().getComponent(); |
||||
var ui = supportsIndicator(c); |
||||
if (ui != null) { |
||||
ui.clearDropIndicator(); |
||||
} |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void dropActionChanged(final DropTargetDragEvent e) { |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Returns the index at which to add a tab if it is dropped at the mouse |
||||
* location specified by <code>p</code>. |
||||
* |
||||
* @param tabbedPane The tabbed pane who would be receiving the tab. |
||||
* @param p The mouse location. |
||||
* @return The index at which to add the tab. |
||||
*/ |
||||
protected int getDroppedTabIndex(final TabTransferable t, @NotNull final JTabbedPane tabbedPane, |
||||
@NotNull final Point p) { |
||||
var tab = tabbedPane.indexAtLocation(p.x, p.y); |
||||
var ui = supportsIndicator(tabbedPane); |
||||
if (ui != null) { |
||||
if (tab == -1) { |
||||
var bounds = ui.getTabAreaBounds(); |
||||
if (bounds.contains(p)) { |
||||
if (tabbedPane.getTabCount() > 0) { |
||||
var minb = ui.getTabBounds(tabbedPane, 0); |
||||
var maxb = ui.getTabBounds(tabbedPane, tabbedPane.getTabCount() - 1); |
||||
if (tabbedPane.getComponentOrientation().isLeftToRight()) { |
||||
int x = Math.max(bounds.x, minb.x); |
||||
bounds.width = Math.min(bounds.x + bounds.width - x, maxb.x + maxb.width - x); |
||||
bounds.x = x; |
||||
} else { |
||||
int x = Math.max(bounds.x, maxb.x); |
||||
bounds.width = Math.min(bounds.x + bounds.width - x, minb.x + minb.width - x); |
||||
bounds.x = x; |
||||
} |
||||
int y = Math.max(bounds.y, minb.y); |
||||
bounds.height = Math.min(bounds.y + bounds.height - y, maxb.x + maxb.height - y); |
||||
} |
||||
|
||||
switch (tabbedPane.getTabPlacement()) { |
||||
case TOP, BOTTOM -> { |
||||
if (tabbedPane.getComponentOrientation().isLeftToRight()) { |
||||
tab = p.x <= bounds.x + bounds.width / 2 ? 0 : tabbedPane.getTabCount(); |
||||
} else { |
||||
tab = p.x >= bounds.x + bounds.width / 2 ? 1 : tabbedPane.getTabCount(); |
||||
} |
||||
} |
||||
case LEFT, RIGHT -> tab = p.y <= bounds.y + bounds.height / 2 ? 0 : tabbedPane.getTabCount(); |
||||
} |
||||
} |
||||
} else { |
||||
if (tab < tabbedPane.getTabCount()) { |
||||
var b = tabbedPane.getBoundsAt(tab); |
||||
|
||||
if (tab >= 1 && !ui.scrollableTabLayoutEnabled()) { |
||||
var prev = tabbedPane.getBoundsAt(tab - 1); |
||||
if (b.y + b.height < mouseLocation.y && |
||||
prev.y <= mouseLocation.y && mouseLocation.y <= prev.y + prev.height) { |
||||
b = prev; |
||||
} |
||||
} |
||||
|
||||
|
||||
var sb = (ui.scrollableTabLayoutEnabled()) ? t.getTabBounds() : EMPTY_RECT; |
||||
switch (tabbedPane.getTabPlacement()) { |
||||
case TOP, BOTTOM -> { |
||||
if (tabbedPane.getComponentOrientation().isLeftToRight()) { |
||||
if (p.x >= b.x + sb.width + (b.width - sb.width) / 2) { |
||||
tab += 1; |
||||
} |
||||
} else { |
||||
if (p.x <= b.x + (b.width - sb.width) / 2) { |
||||
tab += 1; |
||||
} |
||||
} |
||||
} |
||||
case LEFT, RIGHT -> { |
||||
if (p.y >= b.y + sb.height + (b.height - sb.height) / 2) { |
||||
tab += 1; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} else if (tab == -1) { |
||||
tab = tabbedPane.getTabCount(); |
||||
} |
||||
return tab; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* We can only move tabs, we cannot copy them. |
||||
* |
||||
* @param c This parameter is ignored. |
||||
* @return <code>TransferHandler.MOVE</code>, as we can only move tabs. |
||||
*/ |
||||
@Override |
||||
public int getSourceActions(final JComponent c) { |
||||
return MOVE; |
||||
} |
||||
|
||||
|
||||
protected boolean hasTabFlavor(final DataFlavor[] flavors) { |
||||
if (tabFlavor == null) { |
||||
return false; |
||||
} |
||||
for (DataFlavor flavor : flavors) { |
||||
if (tabFlavor.equals(flavor)) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Called when the drag-and-drop operation has just completed. This |
||||
* creates a new tab identical to the one "dragged" and places it in the |
||||
* destination <code>JTabbedPane</code>. |
||||
* |
||||
* @param c The component receiving the "drop" (the instance of |
||||
* <code>JTabbedPane</code>). |
||||
* @param t The data being transfered (information about the tab and the |
||||
* component contained by the tab). |
||||
* @return Whether or not the import was successful. |
||||
*/ |
||||
@Override |
||||
public boolean importData(final JComponent c, @NotNull final Transferable t) { |
||||
|
||||
boolean successful = false; |
||||
if (hasTabFlavor(t.getTransferDataFlavors()) && mouseLocation != null) { |
||||
try { |
||||
JTabbedPane tabbedPane = (JTabbedPane) c; |
||||
int tab = getDroppedTabIndex(currentTransferable, tabbedPane, mouseLocation); |
||||
TabTransferable.TabTransferData td = (TabTransferable.TabTransferData) t.getTransferData(tabFlavor); |
||||
JTabbedPane sourcePane = td.sourceTabbedPane; |
||||
int sourceIndex = td.tabIndex; |
||||
|
||||
if (tabbedPane == sourcePane && sourceIndex == tab) { |
||||
//Nothing to do. Just select the tab to be sure.
|
||||
selectTab(sourcePane, sourceIndex); |
||||
return true; |
||||
} |
||||
if (tab < 0 || tab > tabbedPane.getTabCount()) { |
||||
return false; |
||||
} |
||||
|
||||
String tabName = sourcePane.getTitleAt(sourceIndex); |
||||
Icon icon = sourcePane.getIconAt(sourceIndex); |
||||
Component comp = sourcePane.getComponentAt(sourceIndex); |
||||
String toolTip = sourcePane.getToolTipTextAt(sourceIndex); |
||||
Color foreground = sourcePane.getForegroundAt(sourceIndex); |
||||
Component tabComp = sourcePane.getTabComponentAt(sourceIndex); |
||||
|
||||
tabbedPane.insertTab(tabName, icon, comp, toolTip, tab); |
||||
|
||||
int index = tab; |
||||
if (tabbedPane == sourcePane) { |
||||
if (sourceIndex < index) index--; |
||||
} |
||||
|
||||
if (tabComp != null) { |
||||
tabbedPane.setTabComponentAt(index, tabComp); |
||||
} |
||||
tabbedPane.setForegroundAt(index, foreground); |
||||
selectTab(tabbedPane, index); |
||||
|
||||
successful = true; |
||||
var ui = supportsIndicator(c); |
||||
if (ui != null) { |
||||
ui.clearDropIndicator(); |
||||
} |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
return successful; |
||||
} |
||||
|
||||
/** |
||||
* Selects the specified tab in the specified tabbed pane. This method |
||||
* can be overridden by subclasses to do more stuff than simply select |
||||
* the tab. |
||||
* |
||||
* @param tabbedPane The tabbed pane. |
||||
* @param index The index of the tab to select. |
||||
*/ |
||||
protected void selectTab(final JTabbedPane tabbedPane, final int index) { |
||||
SwingUtilities.invokeLater(() -> { |
||||
tabbedPane.setSelectedIndex(index); |
||||
tabbedPane.requestFocus(); |
||||
}); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Transferable representing a tab from a tabbed pane and its contents. |
||||
*/ |
||||
public class TabTransferable implements Transferable { |
||||
|
||||
private final TabTransferData transferData; |
||||
|
||||
/** |
||||
* The data remembered about the tab. |
||||
*/ |
||||
public class TabTransferData { |
||||
|
||||
private final JTabbedPane sourceTabbedPane; |
||||
private final int tabIndex; |
||||
private final Rectangle tabBounds; |
||||
|
||||
@Contract(pure = true) |
||||
public TabTransferData(@NotNull final JTabbedPane tabbedPane, final int tabIndex) { |
||||
this.sourceTabbedPane = tabbedPane; |
||||
this.tabIndex = tabIndex; |
||||
this.tabBounds = tabbedPane.getBoundsAt(tabIndex); |
||||
} |
||||
|
||||
} |
||||
|
||||
public TabTransferable(@NotNull final JTabbedPane tabbedPane) { |
||||
int index = tabbedPane.getSelectedIndex(); |
||||
transferData = new TabTransferData(tabbedPane, index); |
||||
} |
||||
|
||||
public Rectangle getTabBounds() { |
||||
return new Rectangle(transferData.tabBounds); |
||||
} |
||||
|
||||
@Override |
||||
public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException { |
||||
if (!isDataFlavorSupported(flavor)) { |
||||
throw new UnsupportedFlavorException(flavor); |
||||
} |
||||
return transferData; |
||||
} |
||||
|
||||
@Override |
||||
public DataFlavor[] getTransferDataFlavors() { |
||||
return new DataFlavor[]{tabFlavor}; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isDataFlavorSupported(final DataFlavor flavor) { |
||||
return tabFlavor.equals(flavor); |
||||
} |
||||
|
||||
} |
||||
|
||||
protected class TabbedPaneDragHandler implements DragGestureListener, DragSourceListener { |
||||
|
||||
private boolean scrolls; |
||||
|
||||
// --- DragGestureListener methods -----------------------------------
|
||||
|
||||
/** |
||||
* a Drag gesture has been recognized |
||||
*/ |
||||
public void dragGestureRecognized(@NotNull final DragGestureEvent dge) { |
||||
JComponent c = (JComponent) dge.getComponent(); |
||||
TabbedPaneTransferHandler th = (TabbedPaneTransferHandler) c.getTransferHandler(); |
||||
Transferable t = th.createTransferable(c); |
||||
if (t != null) { |
||||
scrolls = c.getAutoscrolls(); |
||||
c.setAutoscrolls(false); |
||||
try { |
||||
Image im = th.getDragImage(); |
||||
if (im == null) { |
||||
dge.startDrag(Cursor.getDefaultCursor(), t, this); |
||||
} else { |
||||
dge.startDrag(Cursor.getDefaultCursor(), im, th.getDragImageOffset(), t, this); |
||||
} |
||||
return; |
||||
} catch (RuntimeException re) { |
||||
c.setAutoscrolls(scrolls); |
||||
} |
||||
} |
||||
|
||||
th.exportDone(c, t, NONE); |
||||
} |
||||
|
||||
// --- DragSourceListener methods -----------------------------------
|
||||
|
||||
/** |
||||
* as the hotspot enters a platform dependent drop site |
||||
*/ |
||||
public void dragEnter(final DragSourceDragEvent dsde) { |
||||
} |
||||
|
||||
/** |
||||
* as the hotspot moves over a platform dependent drop site |
||||
*/ |
||||
public void dragOver(final DragSourceDragEvent dsde) { |
||||
} |
||||
|
||||
/** |
||||
* as the hotspot exits a platform dependent drop site |
||||
*/ |
||||
public void dragExit(final DragSourceEvent dsde) { |
||||
} |
||||
|
||||
/** |
||||
* as the operation completes |
||||
*/ |
||||
public void dragDropEnd(@NotNull final DragSourceDropEvent dsde) { |
||||
DragSourceContext dsc = dsde.getDragSourceContext(); |
||||
JComponent c = (JComponent) dsc.getComponent(); |
||||
if (dsde.getDropSuccess()) { |
||||
((TabbedPaneTransferHandler) c.getTransferHandler()).exportDone(c, dsc.getTransferable(), |
||||
dsde.getDropAction()); |
||||
} else { |
||||
((TabbedPaneTransferHandler) c.getTransferHandler()).exportDone(c, dsc.getTransferable(), NONE); |
||||
} |
||||
c.setAutoscrolls(scrolls); |
||||
|
||||
var ui = supportsIndicator(currentTransferable.transferData.sourceTabbedPane); |
||||
if (ui != null) { |
||||
ui.clearSourceIndicator(); |
||||
} |
||||
if (!dsde.getDropSuccess()) { |
||||
selectTab(currentTransferable.transferData.sourceTabbedPane, |
||||
currentTransferable.transferData.tabIndex); |
||||
} |
||||
currentTransferable = null; |
||||
} |
||||
|
||||
public void dropActionChanged(final DragSourceDragEvent dsde) { |
||||
} |
||||
} |
||||
|
||||
protected static class TabbedPaneDragGestureRecognizer extends DragGestureRecognizer { |
||||
|
||||
protected TabbedPaneDragGestureRecognizer(final DragGestureListener dgl) { |
||||
super(DragSource.getDefaultDragSource(), null, NONE, dgl); |
||||
} |
||||
|
||||
void gestured(final JComponent c, final MouseEvent e, final int srcActions, final int action) { |
||||
setComponent(c); |
||||
setSourceActions(srcActions); |
||||
appendEvent(e); |
||||
fireDragGestureRecognized(action, e.getPoint()); |
||||
} |
||||
|
||||
/** |
||||
* register this DragGestureRecognizer's Listeners with the Component |
||||
*/ |
||||
protected void registerListeners() { |
||||
} |
||||
|
||||
/** |
||||
* unregister this DragGestureRecognizer's Listeners with the Component |
||||
* <p> |
||||
* subclasses must override this method |
||||
*/ |
||||
protected void unregisterListeners() { |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,63 @@
|
||||
package com.weis.darklaf.util; |
||||
|
||||
import com.weis.darklaf.LogFormatter; |
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.awt.*; |
||||
import java.awt.image.BufferedImage; |
||||
import java.util.logging.ConsoleHandler; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* Image utilities. |
||||
* |
||||
* @author Jannis Weis |
||||
* @since 2018 |
||||
*/ |
||||
public final class ImageUtil { |
||||
|
||||
/** |
||||
* The scaling factor. |
||||
*/ |
||||
public static final double SCALE_X; |
||||
public static final double SCALE_Y; |
||||
private static final Logger LOGGER = Logger.getLogger(ImageUtil.class.getName()); |
||||
|
||||
static { |
||||
LOGGER.setUseParentHandlers(false); |
||||
ConsoleHandler handler = new ConsoleHandler(); |
||||
handler.setFormatter(new LogFormatter()); |
||||
LOGGER.addHandler(handler); |
||||
|
||||
var mode = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode(); |
||||
var screenSize = Toolkit.getDefaultToolkit().getScreenSize(); |
||||
SCALE_X = mode.getWidth() / (double) screenSize.width; |
||||
SCALE_Y = mode.getHeight() / (double) screenSize.height; |
||||
LOGGER.info("Using screen scaling SCALE_X=" + SCALE_X + ", SCALE_Y=" + SCALE_Y); |
||||
} |
||||
|
||||
@Contract(pure = true) |
||||
private ImageUtil() { |
||||
} |
||||
|
||||
/** |
||||
* Create image from component. |
||||
* |
||||
* @param c the component. |
||||
* @param bounds the bounds inside the component to capture. |
||||
* @return image containing the captured area. |
||||
*/ |
||||
@NotNull |
||||
public static Image imageFromComponent(@NotNull final Component c, @NotNull final Rectangle bounds) { |
||||
BufferedImage image = new BufferedImage((int) (SCALE_X * bounds.width), (int) (SCALE_Y * bounds.height), |
||||
BufferedImage.TYPE_INT_RGB); |
||||
final Graphics2D g2d = (Graphics2D) image.getGraphics(); |
||||
g2d.scale(SCALE_Y, SCALE_Y); |
||||
g2d.translate(-bounds.x, -bounds.y); |
||||
c.printAll(g2d); |
||||
|
||||
g2d.dispose(); |
||||
return image; |
||||
} |
||||
} |
@ -0,0 +1,96 @@
|
||||
import com.weis.darklaf.icons.IconLoader; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
|
||||
public class DnDTest extends JFrame { |
||||
|
||||
public DnDTest() { |
||||
|
||||
initUI(); |
||||
} |
||||
|
||||
private void initUI() { |
||||
|
||||
var icon1 = IconLoader.get().getUIAwareIcon("files/folder.svg"); |
||||
var icon2 = IconLoader.get().getUIAwareIcon("files/text.svg"); |
||||
var icon3 = IconLoader.get().getUIAwareIcon("files/unknown.svg"); |
||||
|
||||
var label1 = new JLabel(icon1, JLabel.CENTER); |
||||
var label2 = new JLabel(icon2, JLabel.CENTER); |
||||
var label3 = new JLabel(icon3, JLabel.CENTER); |
||||
|
||||
var listener = new DragMouseAdapter(); |
||||
label1.addMouseListener(listener); |
||||
label2.addMouseListener(listener); |
||||
label3.addMouseListener(listener); |
||||
|
||||
var button = new JButton(icon2); |
||||
button.setFocusable(false); |
||||
|
||||
label1.setTransferHandler(new TransferHandler("icon")); |
||||
label2.setTransferHandler(new TransferHandler("icon")); |
||||
label3.setTransferHandler(new TransferHandler("icon")); |
||||
button.setTransferHandler(new TransferHandler("icon")); |
||||
|
||||
createLayout(label1, label2, label3, button); |
||||
|
||||
setTitle("Icon Drag & Drop"); |
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
||||
setLocationRelativeTo(null); |
||||
} |
||||
|
||||
private class DragMouseAdapter extends MouseAdapter { |
||||
|
||||
public void mousePressed(MouseEvent e) { |
||||
|
||||
var c = (JComponent) e.getSource(); |
||||
var handler = c.getTransferHandler(); |
||||
handler.exportAsDrag(c, e, TransferHandler.COPY); |
||||
} |
||||
} |
||||
|
||||
private void createLayout(JComponent... arg) { |
||||
|
||||
var pane = getContentPane(); |
||||
var gl = new GroupLayout(pane); |
||||
pane.setLayout(gl); |
||||
|
||||
gl.setAutoCreateContainerGaps(true); |
||||
gl.setAutoCreateGaps(true); |
||||
|
||||
gl.setHorizontalGroup(gl.createParallelGroup(GroupLayout.Alignment.CENTER) |
||||
.addGroup(gl.createSequentialGroup() |
||||
.addComponent(arg[0]) |
||||
.addGap(30) |
||||
.addComponent(arg[1]) |
||||
.addGap(30) |
||||
.addComponent(arg[2]) |
||||
) |
||||
.addComponent(arg[3], GroupLayout.DEFAULT_SIZE, |
||||
GroupLayout.DEFAULT_SIZE, Integer.MAX_VALUE) |
||||
); |
||||
|
||||
gl.setVerticalGroup(gl.createSequentialGroup() |
||||
.addGroup(gl.createParallelGroup() |
||||
.addComponent(arg[0]) |
||||
.addComponent(arg[1]) |
||||
.addComponent(arg[2])) |
||||
.addGap(30) |
||||
.addComponent(arg[3]) |
||||
); |
||||
|
||||
pack(); |
||||
} |
||||
|
||||
public static void main(String[] args) { |
||||
|
||||
EventQueue.invokeLater(() -> { |
||||
|
||||
var ex = new DnDTest(); |
||||
ex.setVisible(true); |
||||
}); |
||||
} |
||||
} |
Loading…
Reference in new issue