Browse Source

Added tooltips. Still not done.

pull/1/head
weisj 5 years ago
parent
commit
04330f610d
  1. 149
      src/main/java/com/weis/darklaf/components/border/TextBubbleBorder.java
  2. 2
      src/main/java/com/weis/darklaf/ui/colorchooser/ColorWheelPanel.java
  3. 9
      src/main/java/com/weis/darklaf/ui/colorchooser/RecentSwatchPanel.java
  4. 110
      src/main/java/com/weis/darklaf/ui/colorchooser/SlideComponent.java
  5. 103
      src/main/java/com/weis/darklaf/ui/colorchooser/SwatchPanel.java
  6. 96
      src/main/java/com/weis/darklaf/ui/tooltip/DarkTooltipBorder.java
  7. 120
      src/main/java/com/weis/darklaf/ui/tooltip/DarkTooltipUI.java
  8. 7
      src/main/resources/com/weis/darklaf/darcula.properties
  9. 2
      src/test/java/ColorChooserDemo.java
  10. 41
      src/test/java/ColorModelTest.java
  11. 11
      src/test/java/GenerateColors.java

149
src/main/java/com/weis/darklaf/components/border/TextBubbleBorder.java

@ -1,12 +1,14 @@
package com.weis.darklaf.components.border;
import com.weis.darklaf.components.alignment.Alignment;
import com.weis.darklaf.util.GraphicsUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import javax.swing.border.AbstractBorder;
import java.awt.*;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
/**
@ -17,10 +19,6 @@ import java.awt.geom.RoundRectangle2D;
*/
public class TextBubbleBorder extends AbstractBorder {
private static final long serialVersionUID = 1L;
@NotNull
private final RenderingHints hints;
@NotNull
private final Insets insets;
private Alignment pointerSide = Alignment.NORTH;
private Color color;
@ -58,9 +56,6 @@ public class TextBubbleBorder extends AbstractBorder {
this.radius = radius;
this.pointerSize = pointerSize;
this.pointerWidth = pointerSize;
hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
insets = new Insets(0, 0, 0, 0);
setThickness(thickness);
}
@ -147,7 +142,7 @@ public class TextBubbleBorder extends AbstractBorder {
*/
@NotNull
public TextBubbleBorder setThickness(final int n) {
thickness = n < 0 ? 0 : n;
thickness = Math.max(n, 0);
stroke = new BasicStroke(thickness);
return setPointerSize(pointerSize);
}
@ -190,30 +185,28 @@ public class TextBubbleBorder extends AbstractBorder {
*/
@NotNull
public TextBubbleBorder setPointerSize(final int size) {
pointerSize = size < 0 ? 0 : size;
final int pad = radius / 2 + thickness;
final int pointerSidePad = pad + pointerSize + thickness;
int left = pad;
int right = pad;
int bottom = pad;
int top = pad;
pointerSize = Math.max(size, 0);
int left = thickness;
int right = thickness;
int bottom = thickness;
int top = thickness;
switch (pointerSide) {
case NORTH:
case NORTH_WEST:
case NORTH_EAST:
top = pointerSidePad;
top += pointerSize;
break;
case SOUTH:
case SOUTH_WEST:
case SOUTH_EAST:
bottom = pointerSidePad;
bottom += pointerSize;
break;
case WEST:
left = pointerSidePad;
left += pointerSize;
break;
case EAST:
right = pointerSidePad;
right += pointerSize;
break;
default:
break;
@ -259,107 +252,99 @@ public class TextBubbleBorder extends AbstractBorder {
return getBorderInsets(c);
}
@Override
public void paintBorder(@NotNull final Component c, final Graphics g,
final int x, final int y, final int width, final int height) {
public void paintBorder(final Graphics g, final Area innerArea) {
final Graphics2D g2 = (Graphics2D) g;
var bubble = calculateBubbleRect(width, height);
int pointerPad;
switch (pointerSide) {
case WEST:
case EAST:
pointerPad = (int) (pointerPadPercent * (height - 2 * radius - 5 * pointerSize));
break;
case CENTER:
pointerPad = 0;
break;
default:
pointerPad = (int) (pointerPadPercent * (width - 2 * radius - 5 * pointerSize));
break;
}
final Polygon pointer = creatPointerShape(width, height, pointerPad, bubble);
final Area area = new Area(bubble);
area.add(new Area(pointer));
g2.setRenderingHints(hints);
g2.setColor(c.getBackground());
g2.fill(area);
var config = GraphicsUtil.setupStrokePainting(g);
g2.setColor(color);
g2.setStroke(stroke);
g2.draw(area);
g2.draw(innerArea);
config.restore();
}
@NotNull
@Contract("_, _ -> new")
private RoundRectangle2D.Double calculateBubbleRect(final int width, final int height) {
int rx = thickness;
int ry = thickness;
int rw = width - thickness;
int rh = height - thickness;
@Override
public void paintBorder(@NotNull final Component c, final Graphics g,
final int x, final int y, final int width, final int height) {
var area = getInnerArea(x, y, width, height);
paintBorder(g, area);
}
@Contract(pure = true)
private double calculatePointerPad(final int width, final int height) {
double pointerPad;
switch (pointerSide) {
case WEST:
rx += pointerSize;
rw -= pointerSize + thickness;
rh -= thickness;
break;
case EAST:
rw -= pointerSize;
rh -= thickness;
pointerPad = radius + (height - insets.top - insets.bottom - 2 * radius) / 2.0;
break;
case NORTH:
case NORTH_WEST:
case NORTH_EAST:
ry += pointerSize;
rh -= pointerSize + thickness;
rw -= thickness;
break;
case SOUTH:
case SOUTH_WEST:
case SOUTH_EAST:
rh -= pointerSize;
rw -= thickness;
pointerPad = radius + (pointerPadPercent * (width - insets.left - insets.right - 2 * radius));
break;
default:
pointerPad = 0;
break;
}
return new RoundRectangle2D.Double(rx, ry, rw, rh, radius, radius);
return pointerPad;
}
public Area getInnerArea(final int x, final int y, final int width, final int height) {
var bubble = calculateBubbleRect(x, y, width, height);
final Area area = new Area(bubble);
if (pointerSide != Alignment.CENTER) {
double pointerPad = calculatePointerPad(width, height);
Path2D pointer = creatPointerShape(pointerPad, bubble);
area.add(new Area(pointer));
}
return area;
}
@Contract("_, _, _, _ -> new")
public RoundRectangle2D.@NotNull Double calculateBubbleRect(final int x, final int y,
final int width, final int height) {
return new RoundRectangle2D.Double(x + insets.left, y + insets.top, width - insets.left - insets.right,
height - insets.top - insets.bottom, radius, radius);
}
@NotNull
private Polygon creatPointerShape(final int width, final int height, final int pointerPad,
@NotNull final RoundRectangle2D.Double bubble) {
final int basePad = 2 * pointerSize + thickness + radius + pointerPad;
final int widthPad = pointerWidth / 2;
final Polygon pointer = new Polygon();
private Path2D creatPointerShape(final double pointerPad, @NotNull final RoundRectangle2D.Double bubble) {
final double w = pointerWidth / 2.0;
final Path2D pointer = new Path2D.Double(Path2D.WIND_EVEN_ODD);
double x = bubble.x;
double y = bubble.y;
switch (pointerSide) {
case WEST:
pointer.addPoint((int) bubble.x, basePad - widthPad);// top
pointer.addPoint((int) bubble.x, basePad + pointerSize + widthPad);// bottom
pointer.addPoint(thickness, basePad + pointerSize / 2);
pointer.moveTo(x, y + pointerPad - w); //Top
pointer.lineTo(x - pointerSize, y + pointerPad);
pointer.lineTo(x, y + pointerPad + w);// bottom
break;
case EAST:
int x = (int) (bubble.x + bubble.width);
pointer.addPoint(x, basePad - widthPad);// top
pointer.addPoint(x, basePad + pointerSize + widthPad);// bottom
pointer.addPoint(width - thickness, basePad + pointerSize / 2);
pointer.moveTo(x + bubble.width, y + pointerPad - w);// top
pointer.lineTo(x + bubble.width + pointerSize, y + pointerPad);
pointer.lineTo(x + bubble.width, y + pointerPad + w);// bottom
break;
case NORTH:
case NORTH_WEST:
case NORTH_EAST:
pointer.addPoint(basePad - widthPad, (int) bubble.y);// left
pointer.addPoint(basePad + pointerSize + widthPad, (int) bubble.y);// right
pointer.addPoint(basePad + (pointerSize / 2), thickness);
pointer.moveTo(x + pointerPad - w, y);// left
pointer.lineTo(x + pointerPad, y - pointerSize);
pointer.lineTo(x + pointerPad + w, y);// right
break;
case SOUTH:
case SOUTH_WEST:
case SOUTH_EAST:
int y = (int) (bubble.y + bubble.height);
pointer.addPoint(basePad - widthPad, y);// left
pointer.addPoint(basePad + pointerSize + widthPad, y);// right
pointer.addPoint(basePad + (pointerSize / 2), height - thickness);
pointer.moveTo(x + pointerPad - w, y + bubble.height);// left
pointer.lineTo(x + pointerPad, y + bubble.height + pointerSize);
pointer.lineTo(x + pointerPad + w, y + bubble.height);// right
break;
default:
break;
}
pointer.closePath();
return pointer;
}
}

2
src/main/java/com/weis/darklaf/ui/colorchooser/ColorWheelPanel.java

@ -29,7 +29,7 @@ public class ColorWheelPanel extends JPanel {
myBrightnessComponent = new SlideComponent("Brightness", true);
myBrightnessComponent.setToolTipText("Brightness");
myBrightnessComponent.addListener(value -> {
myColorWheel.setBrightness(1f - (value / 255f));
myColorWheel.setBrightness(1 - (value / 255f));
myColorWheel.repaint();
});

9
src/main/java/com/weis/darklaf/ui/colorchooser/RecentSwatchPanel.java

@ -1,8 +1,11 @@
package com.weis.darklaf.ui.colorchooser;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.Objects;
class RecentSwatchPanel extends SwatchPanel {
private Color defaultRecentColor;
@ -25,16 +28,16 @@ class RecentSwatchPanel extends SwatchPanel {
}
public void setMostRecentColor(final Color c) {
if (colors[0].equals(c)) return;
if (Objects.equals(colors[0], c)) return;
System.arraycopy(colors, 0, colors, 1, colors.length - 1);
colors[0] = c;
repaint();
}
@Override
public String getToolTipText(final MouseEvent e) {
public String getToolTipText(@NotNull final MouseEvent e) {
Color color = getColorForLocation(e.getX(), e.getY());
if (color == defaultRecentColor) return null;
if (color == defaultRecentColor || color == null) return null;
return color.getRed() + ", " + color.getGreen() + ", " + color.getBlue();
}
}

110
src/main/java/com/weis/darklaf/ui/colorchooser/SlideComponent.java

@ -1,5 +1,6 @@
package com.weis.darklaf.ui.colorchooser;
import com.weis.darklaf.components.alignment.Alignment;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@ -22,12 +23,10 @@ class SlideComponent extends JComponent {
private final boolean myVertical;
private final String myTitle;
private final List<Consumer<Integer>> myListeners = new ArrayList<>();
//Todo.
// private LightweightHint myTooltipHint;
private final JLabel myLabel = new JLabel();
private int myPointerValue = 0;
private int myValue = 0;
private Unit myUnit = Unit.LEVEL;
private JToolTip tooltip;
SlideComponent(final String title, final boolean vertical) {
myTitle = title;
@ -41,28 +40,13 @@ class SlideComponent extends JComponent {
});
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(final MouseEvent e) {
processMouse(e);
}
@Override
public void mouseEntered(final MouseEvent e) {
updateBalloonText();
}
@Override
public void mouseMoved(final MouseEvent e) {
updateBalloonText();
}
@Override
public void mouseExited(final MouseEvent e) {
//Todo
// if (myTooltipHint != null) {
// myTooltipHint.hide();
// myTooltipHint = null;
// }
var p = e.getPoint();
p = SwingUtilities.convertPoint(e.getComponent(), p, SlideComponent.this);
if (tooltip != null && !contains(p)) {
tooltip.setVisible(false);
}
}
});
@ -89,15 +73,14 @@ class SlideComponent extends JComponent {
repaint();
}
});
setToolTipText(getToolTipText(null));
}
private static void drawKnob(@NotNull final Graphics2D g2d, int x, int y, final boolean vertical) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//Todo Colors
if (vertical) {
y -= 6;
Polygon arrowShadow = new Polygon();
arrowShadow.addPoint(x - 5, y + 1);
arrowShadow.addPoint(x + 7, y + 7);
@ -138,36 +121,39 @@ class SlideComponent extends JComponent {
myUnit = unit;
}
private void updateBalloonText() {
@Override
public String getToolTipText(final MouseEvent event) {
return myTitle + ": " + Unit.formatValue(myValue, myUnit);
}
@Override
public Point getToolTipLocation(final MouseEvent e) {
if (tooltip == null) {
createToolTip();
tooltip.setTipText(getToolTipText(e));
}
final Point point = myVertical ? new Point(0, myPointerValue) : new Point(myPointerValue, 0);
myLabel.setText(myTitle + ": " + Unit.formatValue(myValue, myUnit));
//Todo
// if (myTooltipHint == null) {
// myTooltipHint = new LightweightHint(myLabel);
// myTooltipHint.setCancelOnClickOutside(false);
// myTooltipHint.setCancelOnOtherWindowOpen(false);
//
// final HintHint hint = new HintHint(this, point)
// .setPreferredPosition(myVertical ? Balloon.Position.atLeft : Balloon
// .Position.above)
// .setBorderColor(Color.BLACK)
// .setAwtTooltip(true)
// .setFont(UIUtil.getLabelFont().deriveFont(Font.BOLD))
// .setTextBg(HintUtil.getInformationColor())
// .setShowImmediately(true);
//
// final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
// myTooltipHint.show(this, point.x, point.y, owner instanceof JComponent ? (JComponent)owner : null, hint);
// }
// else {
// myTooltipHint.setLocation(new RelativePoint(this, point));
// }
if (myVertical) {
point.x -= tooltip.getPreferredSize().width - 7;
point.y -= (tooltip.getPreferredSize().height) / 2 - 4;
} else {
point.x -= tooltip.getPreferredSize().width / 2;
point.y -= tooltip.getPreferredSize().height - 7;
}
return point;
}
@Override
protected void processMouseMotionEvent(final MouseEvent e) {
super.processMouseMotionEvent(e);
updateBalloonText();
public JToolTip createToolTip() {
tooltip = super.createToolTip();
if (myVertical) {
tooltip.setPreferredSize(new Dimension(130, 39));
} else {
tooltip.setPreferredSize(new Dimension(120, 46));
}
tooltip.putClientProperty("JToolTip.insets", new Insets(3, 0, 3, 0));
tooltip.putClientProperty("JToolTip.pointerLocation", myVertical ? Alignment.EAST : Alignment.SOUTH);
return tooltip;
}
private void processMouse(final MouseEvent e) {
@ -198,8 +184,10 @@ class SlideComponent extends JComponent {
return myValue;
}
// 0 - 255
public void setValue(final int value) {
if (value < 0 || value > 255) {
throw new IllegalArgumentException("Value " + value + " not in range [0,255]");
}
myPointerValue = valueToPointerValue(value);
myValue = value;
}
@ -207,8 +195,8 @@ class SlideComponent extends JComponent {
private int pointerValueToValue(int pointerValue) {
pointerValue -= OFFSET;
final int size = myVertical ? getHeight() : getWidth();
float proportion = (size - 23) / 255f;
return Math.round((pointerValue / proportion));
double proportion = (size - 23) / 255f;
return (int) Math.round((pointerValue / proportion));
}
private int valueToPointerValue(final int value) {
@ -229,11 +217,6 @@ class SlideComponent extends JComponent {
: new Dimension(50, 22);
}
@Override
public final void setToolTipText(final String text) {
//disable tooltips
}
@Override
protected void paintComponent(final Graphics g) {
final Graphics2D g2d = (Graphics2D) g;
@ -274,8 +257,11 @@ class SlideComponent extends JComponent {
}
private static String formatValue(final int value, final Unit unit) {
return String.format("%d%s", (int) (getMaxValue(unit) / LEVEL_MAX_VALUE * value),
unit.equals(PERCENT) ? "%" : "");
if (unit == PERCENT) {
return String.format("%d%s", (int) ((getMaxValue(unit) / LEVEL_MAX_VALUE * value)), "%");
} else {
return String.format("%d", (int) (LEVEL_MAX_VALUE - ((getMaxValue(unit) / LEVEL_MAX_VALUE * value))));
}
}
}
}

103
src/main/java/com/weis/darklaf/ui/colorchooser/SwatchPanel.java

@ -1,6 +1,10 @@
package com.weis.darklaf.ui.colorchooser;
import com.weis.darklaf.components.alignment.Alignment;
import com.weis.darklaf.util.GraphicsUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
@ -8,6 +12,7 @@ import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
abstract class SwatchPanel extends JPanel {
@ -19,6 +24,7 @@ abstract class SwatchPanel extends JPanel {
private int selRow;
private int selCol;
private JToolTip tooltip;
public SwatchPanel() {
initValues();
@ -28,7 +34,16 @@ abstract class SwatchPanel extends JPanel {
setBackground(UIManager.getColor("ColorChooser.swatchGridColor"));
setFocusable(true);
setInheritsPopupMenu(true);
addMouseListener(new MouseAdapter() {
@Override
public void mouseExited(final MouseEvent e) {
var p = e.getPoint();
p = SwingUtilities.convertPoint(e.getComponent(), p, SwatchPanel.this);
if (tooltip != null && !contains(p)) {
tooltip.setVisible(false);
}
}
});
addFocusListener(new FocusAdapter() {
public void focusGained(final FocusEvent e) {
repaint();
@ -101,35 +116,43 @@ abstract class SwatchPanel extends JPanel {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
for (int row = 0; row < numSwatches.height; row++) {
int y = row * (swatchSize.height + gap.height);
int y = getYForRow(row);
for (int column = 0; column < numSwatches.width; column++) {
Color c = getColorForCell(column, row);
g.setColor(c);
int x;
if (!this.getComponentOrientation().isLeftToRight()) {
x = (numSwatches.width - column - 1) * (swatchSize.width + gap.width);
} else {
x = column * (swatchSize.width + gap.width);
}
int x = getXForColumn(column);
g.fillRect(x, y, swatchSize.width, swatchSize.height);
if (selRow == row && selCol == column && this.isFocusOwner()) {
Color c2 = new Color(c.getRed() < 125 ? 225 : 30,
c.getGreen() < 125 ? 225 : 30,
c.getBlue() < 125 ? 225 : 30);
if (selRow == row && selCol == column && this.isFocusOwner() && c != null) {
Color c2 = new Color(255 - c.getRed(), 255 - c.getGreen(), 255 - c.getBlue());
g.setColor(c2);
g.drawLine(x, y, x + swatchSize.width, y);
g.drawLine(x, y, x, y + swatchSize.height);
g.drawLine(x + swatchSize.width, y, x + swatchSize.width, y + swatchSize.height);
g.drawLine(x, y + swatchSize.height, x + swatchSize.width, y + swatchSize.height);
g.drawLine(x, y, x + swatchSize.width, y + swatchSize.height);
g.drawLine(x, y + swatchSize.height, x + swatchSize.width, y);
g.fillRect(x, y, swatchSize.width, 1);
g.fillRect(x, y + swatchSize.height - 1, swatchSize.width - 1, 1);
g.fillRect(x, y, 1, swatchSize.height);
g.fillRect(x + swatchSize.width - 1, y, 1, swatchSize.height);
GraphicsUtil.setupStrokePainting(g);
g.drawLine(x + 1, y + 1, x + swatchSize.width - 1, y + swatchSize.height - 1);
g.drawLine(x + 1, y + swatchSize.height - 1, x + swatchSize.width - 1, y + 1);
}
}
}
}
@Contract(pure = true)
private int getYForRow(final int row) {
return row * (swatchSize.height + gap.height);
}
private int getXForColumn(final int column) {
if (!this.getComponentOrientation().isLeftToRight()) {
return (numSwatches.width - column - 1) * (swatchSize.width + gap.width);
} else {
return column * (swatchSize.width + gap.width);
}
}
public Dimension getPreferredSize() {
int x = numSwatches.width * (swatchSize.width + gap.width) - 1;
int y = numSwatches.height * (swatchSize.height + gap.height) - 1;
@ -139,11 +162,36 @@ abstract class SwatchPanel extends JPanel {
protected void initColors() {
}
public String getToolTipText(final MouseEvent e) {
public String getToolTipText(@NotNull final MouseEvent e) {
Color color = getColorForLocation(e.getX(), e.getY());
if (color == null) return null;
return color.getRed() + ", " + color.getGreen() + ", " + color.getBlue();
}
@Override
public Point getToolTipLocation(final MouseEvent e) {
if (tooltip == null) {
createToolTip();
tooltip.setTipText(getToolTipText(e));
}
var p = getCoordinatesForLocation(e.getX(), e.getY());
int x = getXForColumn(p.x);
int y = getYForRow(p.y);
x += swatchSize.width / 2;
y += swatchSize.height / 2;
x -= tooltip.getPreferredSize().width / 2;
return new Point(x, y);
}
@Override
public JToolTip createToolTip() {
tooltip = super.createToolTip();
// tooltip.putClientProperty("JToolTip.pointerWidth", 10);
// tooltip.putClientProperty("JToolTip.pointerHeight", 7);
tooltip.putClientProperty("JToolTip.pointerLocation", Alignment.NORTH);
return tooltip;
}
public void setSelectedColorFromLocation(final int x, final int y) {
if (!this.getComponentOrientation().isLeftToRight()) {
selCol = numSwatches.width - x / (swatchSize.width + gap.width) - 1;
@ -154,7 +202,7 @@ abstract class SwatchPanel extends JPanel {
repaint();
}
public Color getColorForLocation(final int x, final int y) {
public Point getCoordinatesForLocation(final int x, final int y) {
int column;
if (!this.getComponentOrientation().isLeftToRight()) {
column = numSwatches.width - x / (swatchSize.width + gap.width) - 1;
@ -162,10 +210,19 @@ abstract class SwatchPanel extends JPanel {
column = x / (swatchSize.width + gap.width);
}
int row = y / (swatchSize.height + gap.height);
return getColorForCell(column, row);
return new Point(column, row);
}
public Color getColorForLocation(final int x, final int y) {
var p = getCoordinatesForLocation(x, y);
return getColorForCell(p.x, p.y);
}
@Nullable
@Contract(pure = true)
private Color getColorForCell(final int column, final int row) {
return colors[(row * numSwatches.width) + column]; // (STEVE) - change data orientation here
int index = (row * numSwatches.width) + column;
if (index >= colors.length) return null;
return colors[(row * numSwatches.width) + column];
}
}

96
src/main/java/com/weis/darklaf/ui/tooltip/DarkTooltipBorder.java

@ -0,0 +1,96 @@
package com.weis.darklaf.ui.tooltip;
import com.weis.darklaf.components.alignment.Alignment;
import com.weis.darklaf.components.border.TextBubbleBorder;
import org.jdesktop.swingx.border.DropShadowBorder;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.plaf.UIResource;
import java.awt.*;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
public class DarkTooltipBorder implements Border, UIResource {
private final DropShadowBorder shadowBorder = new DropShadowBorder(Color.BLACK, 10, 0.4f, 10,
false, true,
true, true);
private final TextBubbleBorder bubbleBorder;
public DarkTooltipBorder() {
bubbleBorder = new TextBubbleBorder(UIManager.getColor("Tooltip.borderColor"));
bubbleBorder.setThickness(1);
bubbleBorder.setPointerSize(8);
bubbleBorder.setPointerWidth(12);
bubbleBorder.setPointerSide(Alignment.EAST);
}
public Area getBackgroundArea(final int width, final int height) {
var ins = shadowBorder.getBorderInsets(null);
return bubbleBorder.getInnerArea(ins.left, ins.top,
width - ins.left - ins.right,
height - ins.top - ins.bottom);
}
@Override
public void paintBorder(final Component c, final Graphics g,
final int x, final int y, final int width, final int height) {
if (c instanceof JToolTip && ((JToolTip) c).getTipText() == null) return;
var ins = shadowBorder.getBorderInsets(c);
var bubbleArea = bubbleBorder.getInnerArea(x + ins.left, y + ins.top,
width - ins.left - ins.right,
height - ins.top - ins.bottom);
var oldClip = g.getClip();
var clip = new Area(new Rectangle2D.Double(x, y, width, height));
clip.subtract(bubbleArea);
g.setClip(clip);
ins = bubbleBorder.getBorderInsets(c);
shadowBorder.paintBorder(c, g, x + ins.left, y + ins.top,
width - ins.left - ins.right, height - ins.top - ins.bottom);
g.setClip(oldClip);
bubbleBorder.paintBorder(g, bubbleArea);
}
@Override
public Insets getBorderInsets(final Component c) {
var ins = (Insets) bubbleBorder.getBorderInsets(c).clone();
var si = shadowBorder.getBorderInsets(c);
var uIns = getUserInsets(c);
ins.left += 5 + si.left + uIns.left;
ins.top += 2 + si.top + uIns.top;
ins.right += 5 + si.right + uIns.right;
ins.bottom += 2 + si.bottom + uIns.bottom;
return ins;
}
protected Insets getUserInsets(final Component c) {
if (c instanceof JComponent) {
var obj = ((JComponent) c).getClientProperty("JToolTip.insets");
if (obj instanceof Insets) {
return (Insets) obj;
}
}
return new Insets(0, 0, 0, 0);
}
@Override
public boolean isBorderOpaque() {
return false;
}
public void setPointerLocation(final Alignment side) {
bubbleBorder.setPointerSide(side);
}
public void setPointerWidth(final int width) {
bubbleBorder.setPointerWidth(width);
}
public void setPointerHeight(final int height) {
bubbleBorder.setPointerSize(height);
}
}

120
src/main/java/com/weis/darklaf/ui/tooltip/DarkTooltipUI.java

@ -0,0 +1,120 @@
package com.weis.darklaf.ui.tooltip;
import com.weis.darklaf.components.alignment.Alignment;
import com.weis.darklaf.util.DarkUIUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicToolTipUI;
import javax.swing.text.View;
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class DarkTooltipUI extends BasicToolTipUI implements PropertyChangeListener {
@NotNull
@Contract("_ -> new")
public static ComponentUI createUI(@NotNull final JComponent c) {
if (Boolean.TRUE.equals(c.getClientProperty("JComponent.plainTooltip"))) {
return BasicToolTipUI.createUI(c);
} else {
return new DarkTooltipUI();
}
}
@Override
public void installUI(final JComponent c) {
super.installUI(c);
}
@Override
public void paint(@NotNull final Graphics g, @NotNull final JComponent c) {
if (((JToolTip) c).getTipText() == null) return;
g.setColor(c.getBackground());
if (c.getBorder() instanceof DarkTooltipBorder) {
var area = ((DarkTooltipBorder) c.getBorder()).getBackgroundArea(c.getWidth(), c.getHeight());
((Graphics2D) g).fill(area);
}
super.paint(g, c);
}
@Override
protected void installDefaults(final JComponent c) {
super.installDefaults(c);
c.setOpaque(false);
}
@Override
protected void installListeners(final JComponent c) {
super.installListeners(c);
c.addHierarchyListener(e -> {
var w = SwingUtilities.getWindowAncestor(c);
if (w != null && !c.isLightweight() && !isDecorated(w)) {
w.setBackground(DarkUIUtil.TRANSPARENT_COLOR);
}
});
c.addPropertyChangeListener(this);
}
protected boolean isDecorated(final Window w) {
if (w instanceof Dialog) {
return !((Dialog) w).isUndecorated();
}
if (w instanceof Frame) {
return !((Frame) w).isUndecorated();
}
return false;
}
public Dimension getPreferredSize(@NotNull final JComponent c) {
Font font = c.getFont();
FontMetrics fm = c.getFontMetrics(font);
Insets insets = c.getInsets();
Dimension prefSize = new Dimension(insets.left + insets.right,
insets.top + insets.bottom);
String text = ((JToolTip) c).getTipText();
if ((text != null) && !text.equals("")) {
View v = (View) c.getClientProperty("html");
if (v != null) {
prefSize.width += (int) v.getPreferredSpan(View.X_AXIS) + 6;
prefSize.height += (int) v.getPreferredSpan(View.Y_AXIS);
} else {
prefSize.width += fm.stringWidth(text) + 6;
prefSize.height += fm.getHeight();
}
}
return prefSize;
}
@Override
public void propertyChange(final PropertyChangeEvent evt) {
var key = evt.getPropertyName();
if (evt.getSource() instanceof JToolTip) {
var tooltip = (JToolTip) evt.getSource();
if (tooltip.getBorder() instanceof DarkTooltipBorder) {
var border = (DarkTooltipBorder) tooltip.getBorder();
var newVal = evt.getNewValue();
if ("JToolTip.pointerLocation".equals(key)) {
if (newVal instanceof Alignment) {
border.setPointerLocation((Alignment) newVal);
} else {
border.setPointerLocation(Alignment.CENTER);
}
} else if ("JToolTip.pointerHeight".equals(key)) {
if (newVal instanceof Integer) {
border.setPointerHeight((Integer) newVal);
}
} else if ("JToolTip.pointerWidth".equals(key)) {
if (newVal instanceof Integer) {
border.setPointerWidth((Integer) newVal);
}
}
}
}
}
}

7
src/main/resources/com/weis/darklaf/darcula.properties

@ -408,8 +408,11 @@ StatusBarUI = com.weis.darklaf.ui.
StatusBar.topColor = 555555
StatusBar.background = 3c3f41
ToolTip.background = 3C3f41
#ToolTip
ToolTipUI = com.weis.darklaf.ui.tooltip.DarkTooltipUI
Tooltip.background = 4B4D4D
Tooltip.borderColor = 5B5D5F
ToolTip.border = com.weis.darklaf.ui.tooltip.DarkTooltipBorder
#InternalFrame
InternalFrameUI = com.bulenkov.darcula.ui.DarculaInternalFrameUI

2
src/test/java/ColorChooserDemo.java

@ -9,7 +9,7 @@ public final class ColorChooserDemo {
SwingUtilities.invokeLater(() -> {
LafManager.loadLaf(LafManager.Theme.Dark);
JColorChooser.showDialog(null, "Color Chooser without transparency",
Color.RED, false);
Color.RED, true);
});
}
}

41
src/test/java/ColorModelTest.java

@ -1,41 +0,0 @@
import com.weis.darklaf.color.DarkColorModel;
import com.weis.darklaf.color.DarkColorModelCMYK;
import com.weis.darklaf.color.DarkColorModelHSB;
import com.weis.darklaf.color.DarkColorModelHSL;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.util.Arrays;
public class ColorModelTest {
public static void main(final String[] args) {
var color = new Color(10, 20, 30);
var m1 = new DarkColorModel();
var m2 = new DarkColorModelHSB();
var m3 = new DarkColorModelHSL();
var m4 = new DarkColorModelCMYK();
// test(m1);
test(m2);
// test(m3);
// test(m4);
}
private static void test(@NotNull final DarkColorModel model) {
System.out.println("Testing " + model.toString());
for (int r = model.getMinimum(0); r < model.getMaximum(0); r++) {
for (int g = model.getMinimum(1); g < model.getMaximum(1); g++) {
for (int b = model.getMinimum(2); b < model.getMaximum(2); b++) {
var c = new int[]{r, g, b};
var interm = model.getColorFromValues(c);
var nc = model.getValuesFromColor(interm);
if (!Arrays.equals(c, nc)) {
throw new RuntimeException(
"Not equals " + Arrays.toString(c) + " : " + interm + " : " + Arrays.toString(nc));
}
}
}
}
}
}

11
src/test/java/GenerateColors.java

@ -2,15 +2,18 @@ import com.weis.darklaf.color.DarkColorModelHSL;
import java.awt.*;
public class GenerateColors {
/**
* Usd to generate the color swatches for ColorChooser.
*/
public final class GenerateColors {
public static void main(final String[] args) {
int cols = 30;
int rows = 20;
System.out.println("{");
int r = 255;
int g = 255;
int b = 255;
int r;
int g;
int b;
for (int i = 1; i < rows + 1; i++) {
r = g = b = (int) ((i - 1) * 255.0 / (rows - 1));
System.out.println(r + "," + g + "," + b + ",");

Loading…
Cancel
Save