mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-165089 refactor breadcrumbs from rectangles to arrows
This commit is contained in:
+291
-198
@@ -18,6 +18,7 @@ package com.intellij.ui.components.breadcrumbs;
|
||||
import com.intellij.openapi.editor.markup.EffectType;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.ColorUtil;
|
||||
import com.intellij.ui.paint.EffectPainter;
|
||||
import com.intellij.ui.paint.RectanglePainter;
|
||||
@@ -27,45 +28,50 @@ import com.intellij.util.ui.MouseEventHandler;
|
||||
import org.intellij.lang.annotations.JdkConstants.FontStyle;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Insets;
|
||||
import java.awt.LinearGradientPaint;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.border.AbstractBorder;
|
||||
|
||||
import static com.intellij.ide.ui.AntialiasingType.getKeyForCurrentScope;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static javax.swing.SwingConstants.LEFT;
|
||||
import static javax.swing.SwingConstants.RIGHT;
|
||||
import static javax.swing.SwingConstants.CENTER;
|
||||
import static javax.swing.SwingUtilities.isLeftMouseButton;
|
||||
import static javax.swing.SwingUtilities.layoutCompoundLabel;
|
||||
|
||||
/**
|
||||
* @author Sergey.Malenkov
|
||||
*/
|
||||
public class Breadcrumbs extends JComponent {
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private final MouseHandler containerMouseHandler = new MouseHandler(event -> getCrumb(event.getX(), event.getY()));
|
||||
private final MouseHandler componentMouseHandler = new MouseHandler(event -> getCrumb(event.getComponent()));
|
||||
|
||||
private BiConsumer<Crumb, InputEvent> hover = ((crumb, event) -> hovered = crumb);
|
||||
private BiConsumer<Crumb, InputEvent> select = ((crumb, event) -> selected = crumb);
|
||||
|
||||
private final ArrayList<CrumbView> views = new ArrayList<>();
|
||||
private final Font[] cache = new Font[4];
|
||||
|
||||
private Crumb last;
|
||||
private LinearGradientPaint gradient;
|
||||
private Crumb hovered;
|
||||
private Crumb selected;
|
||||
|
||||
public Breadcrumbs() {
|
||||
addMouseListener(containerMouseHandler);
|
||||
addMouseMotionListener(containerMouseHandler);
|
||||
MouseHandler handler = new MouseHandler();
|
||||
addMouseListener(handler);
|
||||
addMouseMotionListener(handler);
|
||||
setLayout(STATELESS_LAYOUT);
|
||||
setOpaque(true);
|
||||
}
|
||||
@@ -87,49 +93,57 @@ public class Breadcrumbs extends JComponent {
|
||||
}
|
||||
|
||||
public boolean isAfterSelected(Crumb crumb) {
|
||||
if (last == null || isSelected(last)) return false;
|
||||
for (Crumb current : getCrumbs()) {
|
||||
if (current == crumb) return false;
|
||||
if (isSelected(current)) break;
|
||||
for (CrumbView view : views) {
|
||||
if (view.crumb != null) {
|
||||
if (view.crumb == crumb) return false;
|
||||
if (isSelected(view.crumb)) break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Crumb getCrumbAt(int x, int y) {
|
||||
for (CrumbView view : views) {
|
||||
if (view.contains(x, y)) return view.crumb;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Iterable<Crumb> getCrumbs() {
|
||||
return getComponents(this, Breadcrumbs::getCrumb);
|
||||
return views.stream().map(view -> view.crumb).filter(crumb -> crumb != null).collect(toList());
|
||||
}
|
||||
|
||||
public void setCrumbs(Iterable<? extends Crumb> crumbs) {
|
||||
ArrayList<CrumbLabel> labels = getComponents(this, Breadcrumbs::toCrumbLabel);
|
||||
last = null;
|
||||
CrumbView view = null;
|
||||
int index = 0;
|
||||
if (crumbs != null) {
|
||||
for (Crumb crumb : crumbs) {
|
||||
if (crumb != null) {
|
||||
last = crumb;
|
||||
if (index < labels.size()) {
|
||||
labels.get(index++).setCrumb(crumb);
|
||||
if (index < views.size()) {
|
||||
view = views.get(index++);
|
||||
view.initialize(crumb);
|
||||
}
|
||||
else {
|
||||
CrumbLabel label = new CrumbLabel();
|
||||
label.setCrumb(crumb);
|
||||
label.setBorder(STATELESS_BORDER);
|
||||
label.addMouseListener(componentMouseHandler);
|
||||
label.addMouseMotionListener(componentMouseHandler);
|
||||
add(label);
|
||||
view = new CrumbView(view, crumb);
|
||||
views.add(view);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (index < labels.size()) {
|
||||
labels.get(index++).setCrumb(null);
|
||||
while (index < views.size()) {
|
||||
views.get(index++).initialize(null);
|
||||
}
|
||||
select.accept(last, null);
|
||||
select.accept(view == null ? null : view.crumb, null);
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText(MouseEvent event) {
|
||||
return hovered == null ? null : hovered.getTooltip();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
// this custom component does not have a corresponding UI,
|
||||
@@ -138,57 +152,17 @@ public class Breadcrumbs extends JComponent {
|
||||
g.setColor(getBackground());
|
||||
g.fillRect(0, 0, getWidth(), getHeight());
|
||||
}
|
||||
super.paintComponent(g);
|
||||
}
|
||||
|
||||
protected void paint(Graphics2D g, int x, int y, int width, int height, Crumb crumb) {
|
||||
int scale = getScale(this);
|
||||
EffectType type = getEffectType(crumb);
|
||||
Color color = getEffectColor(crumb);
|
||||
Color background = getBackground(crumb);
|
||||
if (color != null && type != null) {
|
||||
EffectPainter painter = getEffectPainter(type);
|
||||
if (painter != null) {
|
||||
if (background != null) {
|
||||
g.setColor(background);
|
||||
g.fillRect(x + 2 * scale, y + 2 * scale, width - 4 * scale, height - 4 * scale);
|
||||
}
|
||||
Font font = getFont(crumb);
|
||||
if (font != null) {
|
||||
FontMetrics metrics = g.getFontMetrics(font);
|
||||
if (metrics != null) {
|
||||
int descent = metrics.getDescent();
|
||||
int baseline = y + height - 2 * scale - descent;
|
||||
if (painter == EffectPainter.STRIKE_THROUGH) descent = metrics.getAscent();
|
||||
|
||||
g.setColor(color);
|
||||
painter.paint(g, x + 5 * scale, baseline, width - 10 * scale, descent, font);
|
||||
}
|
||||
}
|
||||
if (g instanceof Graphics2D) {
|
||||
Graphics2D g2d = (Graphics2D)g;
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, getKeyForCurrentScope(!Registry.is("editor.breadcrumbs.system.font")));
|
||||
for (CrumbView view : views) {
|
||||
if (view.crumb != null) view.paint(g2d);
|
||||
}
|
||||
else if (type == EffectType.ROUNDED_BOX) {
|
||||
RectanglePainter.paint(g, x + scale, y + scale, width - 2 * scale, height - 2 * scale, height / 2, background, color);
|
||||
}
|
||||
else if (type == EffectType.BOXED) {
|
||||
RectanglePainter.paint(g, x + scale, y + scale, width - 2 * scale, height - 2 * scale, 0, background, color);
|
||||
}
|
||||
}
|
||||
else if (background != null) {
|
||||
g.setColor(background);
|
||||
g.fillRect(x, y, width, height);
|
||||
if (isSelected(crumb)) {
|
||||
paint(g, x, y, width, height, crumb, scale * 2);
|
||||
}
|
||||
}
|
||||
else if (isSelected(crumb)) {
|
||||
paint(g, x, y, width, height, crumb, scale * 2);
|
||||
}
|
||||
else if (isHovered(crumb)) {
|
||||
paint(g, x, y, width, height, crumb, scale);
|
||||
}
|
||||
}
|
||||
|
||||
protected void paint(Graphics2D g, int x, int y, int width, int height, Crumb crumb, int thickness) {
|
||||
protected void paintMarker(Graphics2D g, int x, int y, int width, int height, Crumb crumb, int thickness) {
|
||||
if (thickness > 0) {
|
||||
Color foreground = getMarkerForeground(crumb);
|
||||
if (foreground != null) {
|
||||
@@ -200,7 +174,8 @@ public class Breadcrumbs extends JComponent {
|
||||
|
||||
protected Color getMarkerForeground(Crumb crumb) {
|
||||
if (!Registry.is("editor.breadcrumbs.marker")) return null;
|
||||
Color foreground = getForeground(crumb);
|
||||
CrumbView found = getCrumbView(view -> view.crumb == crumb);
|
||||
Color foreground = found == null ? null : found.foreground;
|
||||
if (foreground == null) return null;
|
||||
double alpha = Registry.doubleValue("editor.breadcrumbs.marker.alpha");
|
||||
return ColorUtil.toAlpha(foreground, (int)(alpha * foreground.getAlpha()));
|
||||
@@ -267,144 +242,102 @@ public class Breadcrumbs extends JComponent {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Crumb getCrumb(int x, int y) {
|
||||
return getCrumb(getComponentAt(x, y));
|
||||
private CrumbView getCrumbView(Predicate<CrumbView> predicate) {
|
||||
for (CrumbView view : views) if (view.crumb != null && predicate.test(view)) return view;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Crumb getCrumb(Component component) {
|
||||
CrumbLabel label = toCrumbLabel(component);
|
||||
return label == null ? null : label.crumb;
|
||||
}
|
||||
|
||||
private static CrumbLabel toCrumbLabel(Component component) {
|
||||
return component instanceof CrumbLabel ? (CrumbLabel)component : null;
|
||||
}
|
||||
|
||||
private static Component toVisible(Component component) {
|
||||
return component.isVisible() ? component : null;
|
||||
}
|
||||
|
||||
private static Component toVisibleAndValid(Component component) {
|
||||
return component.isVisible() && component.isValid() ? component : null;
|
||||
}
|
||||
|
||||
private static <T> ArrayList<T> getComponents(Container container, Function<Component, T> function) {
|
||||
synchronized (container.getTreeLock()) {
|
||||
int count = container.getComponentCount();
|
||||
ArrayList<T> components = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
T component = function.apply(container.getComponent(i));
|
||||
if (component != null) components.add(component);
|
||||
}
|
||||
return components;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dimension getPreferredSize(Iterable<Component> components) {
|
||||
Dimension size = new Dimension();
|
||||
for (Component component : components) {
|
||||
Dimension preferred = component.getPreferredSize();
|
||||
size.width += preferred.width;
|
||||
if (size.height < preferred.height) {
|
||||
size.height = preferred.height;
|
||||
private void layout(boolean update) {
|
||||
Rectangle bounds = new Rectangle(getWidth(), getHeight());
|
||||
JBInsets.removeFrom(bounds, getInsets());
|
||||
int scale = getScale();
|
||||
for (CrumbView view : views) {
|
||||
if (view.crumb != null) {
|
||||
if (update || view.font == null) view.update();
|
||||
bounds.x += getGap(view, scale);
|
||||
view.setBounds(bounds.x, bounds.y, view.preferred.width, bounds.height, scale);
|
||||
bounds.x += view.preferred.width;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
private static int getPreferredHeight(Component component) {
|
||||
Font font = component.getFont();
|
||||
if (font == null) return 0;
|
||||
|
||||
FontMetrics metrics = component.getFontMetrics(font);
|
||||
int height = metrics != null ? metrics.getHeight() : font.getSize();
|
||||
|
||||
Insets insets = getCrumbInsets(null, getScale(font));
|
||||
return insets.top + insets.bottom + height;
|
||||
}
|
||||
|
||||
@SuppressWarnings("UseDPIAwareInsets")
|
||||
private static Insets getCrumbInsets(Insets insets, int scale) {
|
||||
if (insets == null) insets = new Insets(0, 0, 0, 0);
|
||||
insets.top += 2 * scale;
|
||||
insets.left += 5 * scale;
|
||||
insets.right += 5 * scale;
|
||||
insets.bottom += 2 * scale;
|
||||
return insets;
|
||||
}
|
||||
|
||||
private static int getGap(Component component) {
|
||||
return 10 * getScale(component);
|
||||
}
|
||||
|
||||
private static int getScale(Component component) {
|
||||
return component == null ? 1 : getScale(component.getFont());
|
||||
}
|
||||
|
||||
private static int getScale(Font font) {
|
||||
return font == null ? 1 : getScale(font.getSize());
|
||||
}
|
||||
|
||||
private static int getScale(int height) {
|
||||
return height <= 10 ? 1 : (height / 10);
|
||||
}
|
||||
|
||||
private static final AbstractBorder STATELESS_BORDER = new AbstractBorder() {
|
||||
@Override
|
||||
public Insets getBorderInsets(Component component, Insets insets) {
|
||||
return getCrumbInsets(insets, component == null ? 1 : getScale(component.getParent()));
|
||||
if (Registry.is("editor.breadcrumbs.marker")) {
|
||||
gradient = null;
|
||||
}
|
||||
};
|
||||
else {
|
||||
Color color = getForeground();
|
||||
Color alpha = ColorUtil.toAlpha(color, 0);
|
||||
Color[] colors = {alpha, color, alpha};
|
||||
float[] floats = {.2f, .5f, .8f};
|
||||
gradient = new LinearGradientPaint(0, bounds.y, 0, bounds.y + bounds.height, floats, colors);
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePreferredSize(Dimension size, int scale) {
|
||||
for (CrumbView view : views) {
|
||||
if (view.crumb != null) {
|
||||
if (view.font == null) view.update();
|
||||
size.width += view.preferred.width + getGap(view, scale);
|
||||
if (size.height < view.preferred.height) {
|
||||
size.height = view.preferred.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (size.width > 0) {
|
||||
size.width += getGap(null, scale);
|
||||
}
|
||||
if (size.height == 0) {
|
||||
Font font = getFont();
|
||||
if (font != null) {
|
||||
FontMetrics fm = getFontMetrics(font);
|
||||
size.height = 2 * getTopBottom(scale) + (fm != null ? fm.getHeight() : font.getSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getGap(CrumbView view, int scale) {
|
||||
return Registry.is("editor.breadcrumbs.marker")
|
||||
? (view != null && view.parent != null ? 10 * scale : 0)
|
||||
: (view != null && view.parent != null ? 14 * scale : 9 * scale);
|
||||
}
|
||||
|
||||
private static int getLeftRight(int scale) {
|
||||
return 5 * scale;
|
||||
}
|
||||
|
||||
private static int getTopBottom(int scale) {
|
||||
return 2 * scale;
|
||||
}
|
||||
|
||||
private int getScale() {
|
||||
Font font = getFont();
|
||||
if (font != null) {
|
||||
int size = font.getSize();
|
||||
if (size > 10) return size / 10;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static final AbstractLayoutManager STATELESS_LAYOUT = new AbstractLayoutManager() {
|
||||
@Override
|
||||
public void invalidateLayout(Container container) {
|
||||
getComponents(container, Breadcrumbs::toVisibleAndValid).forEach(Component::invalidate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension preferredLayoutSize(Container container) {
|
||||
ArrayList<Component> components = getComponents(container, Breadcrumbs::toVisible);
|
||||
Dimension size = getPreferredSize(components);
|
||||
size.width += components.size() * getGap(container);
|
||||
if (size.height == 0) size.height = getPreferredHeight(container);
|
||||
Dimension size = new Dimension();
|
||||
if (container instanceof Breadcrumbs) {
|
||||
Breadcrumbs breadcrumbs = (Breadcrumbs)container;
|
||||
breadcrumbs.updatePreferredSize(size, breadcrumbs.getScale());
|
||||
}
|
||||
JBInsets.addTo(size, container.getInsets());
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void layoutContainer(Container container) {
|
||||
ArrayList<Component> components = getComponents(container, Breadcrumbs::toVisible);
|
||||
if (!components.isEmpty()) {
|
||||
Dimension size = getPreferredSize(components);
|
||||
Rectangle bounds = new Rectangle(container.getWidth(), container.getHeight());
|
||||
JBInsets.removeFrom(bounds, container.getInsets());
|
||||
int count = components.size();
|
||||
if (count == 1) {
|
||||
if (bounds.width > size.width) bounds.width = size.width;
|
||||
components.get(0).setBounds(bounds);
|
||||
}
|
||||
else /*if (bounds.width < size.width) {
|
||||
}
|
||||
else*/ {
|
||||
int gap = getGap(container);
|
||||
for (Component component : components) {
|
||||
Dimension preferred = component.getPreferredSize();
|
||||
component.setBounds(bounds.x, bounds.y, preferred.width, bounds.height);
|
||||
bounds.x += preferred.width + gap;
|
||||
}
|
||||
}
|
||||
if (container instanceof Breadcrumbs) {
|
||||
Breadcrumbs breadcrumbs = (Breadcrumbs)container;
|
||||
breadcrumbs.layout(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final class MouseHandler extends MouseEventHandler {
|
||||
private final Function<MouseEvent, Crumb> function;
|
||||
|
||||
MouseHandler(Function<MouseEvent, Crumb> function) {
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handle(MouseEvent event) {
|
||||
if (!event.isConsumed()) {
|
||||
@@ -413,22 +346,182 @@ public class Breadcrumbs extends JComponent {
|
||||
switch (event.getID()) {
|
||||
case MouseEvent.MOUSE_MOVED:
|
||||
case MouseEvent.MOUSE_ENTERED:
|
||||
crumb = function.apply(event);
|
||||
crumb = getCrumbAt(event.getX(), event.getY());
|
||||
case MouseEvent.MOUSE_EXITED:
|
||||
if (!isHovered(crumb)) consumer = hover;
|
||||
break;
|
||||
case MouseEvent.MOUSE_CLICKED:
|
||||
if (!isLeftMouseButton(event)) break;
|
||||
crumb = function.apply(event);
|
||||
crumb = getCrumbAt(event.getX(), event.getY());
|
||||
if (crumb != null) consumer = select;
|
||||
break;
|
||||
}
|
||||
if (consumer != null) {
|
||||
consumer.accept(crumb, event);
|
||||
event.consume();
|
||||
layout(true);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class CrumbView {
|
||||
private final Rectangle bounds = new Rectangle();
|
||||
private final Dimension preferred = new Dimension();
|
||||
|
||||
private final CrumbView parent;
|
||||
private Crumb crumb;
|
||||
private Icon icon;
|
||||
private String text;
|
||||
private Path2D path;
|
||||
|
||||
private Font font;
|
||||
private Color foreground;
|
||||
private Color background;
|
||||
private Color effectColor;
|
||||
private EffectType effectType;
|
||||
|
||||
CrumbView(CrumbView parent, Crumb crumb) {
|
||||
this.parent = parent;
|
||||
initialize(crumb);
|
||||
}
|
||||
|
||||
void initialize(Crumb crumb) {
|
||||
this.crumb = crumb;
|
||||
icon = crumb == null ? null : crumb.getIcon();
|
||||
text = crumb == null ? null : crumb.getText();
|
||||
path = null;
|
||||
font = null;
|
||||
}
|
||||
|
||||
private void update() {
|
||||
foreground = getForeground(crumb);
|
||||
background = getBackground(crumb);
|
||||
effectType = getEffectType(crumb);
|
||||
effectColor = getEffectColor(crumb);
|
||||
|
||||
Font font = getFont(crumb);
|
||||
if (font == null) font = getFont();
|
||||
|
||||
if (this.font != font) {
|
||||
this.font = font;
|
||||
|
||||
int scale = getScale();
|
||||
preferred.width = 2 * getLeftRight(scale);
|
||||
preferred.height = 2 * getTopBottom(scale);
|
||||
|
||||
if (font != null && !StringUtil.isEmpty(text)) {
|
||||
FontMetrics fm = getFontMetrics(font);
|
||||
Rectangle iconR = new Rectangle();
|
||||
Rectangle textR = new Rectangle();
|
||||
layout(fm, iconR, textR, new Rectangle(Short.MAX_VALUE, Short.MAX_VALUE));
|
||||
preferred.width += Math.max(iconR.x + iconR.width, textR.x + textR.width) - Math.min(iconR.x, textR.x);
|
||||
preferred.height += Math.max(iconR.y + iconR.height, textR.y + textR.height) - Math.min(iconR.y, textR.y);
|
||||
}
|
||||
else if (icon != null) {
|
||||
preferred.width += icon.getIconWidth();
|
||||
preferred.height += icon.getIconHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String layout(FontMetrics fm, Rectangle iconR, Rectangle textR, Rectangle viewR) {
|
||||
int gap = icon == null ? 0 : icon.getIconWidth() / 4;
|
||||
return layoutCompoundLabel(fm, text, icon, CENTER, LEFT, CENTER, RIGHT, viewR, iconR, textR, gap);
|
||||
}
|
||||
|
||||
private Rectangle getBounds(int dx, int dy) {
|
||||
return new Rectangle(bounds.x + dx, bounds.y + dy, bounds.width - dx - dx, bounds.height - dy - dy);
|
||||
}
|
||||
|
||||
private void setBounds(int x, int y, int width, int height, int scale) {
|
||||
bounds.setBounds(x, y, width, height);
|
||||
if (Registry.is("editor.breadcrumbs.marker")) {
|
||||
path = null;
|
||||
}
|
||||
else {
|
||||
int left = getGap(this, scale);
|
||||
int right = getGap(null, scale);
|
||||
path = new Path2D.Double();
|
||||
path.moveTo(x - left, y);
|
||||
path.lineTo(x + width, y);
|
||||
path.lineTo(x + width + right, y + height * 0.5);
|
||||
path.lineTo(x + width, y + height);
|
||||
path.lineTo(x - left, y + height);
|
||||
if (parent != null) path.lineTo(x - left + right, y + height * 0.5);
|
||||
path.closePath();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean contains(int x, int y) {
|
||||
return crumb != null && (path != null ? path.contains(x, y) : bounds.contains(x, y));
|
||||
}
|
||||
|
||||
private void paint(Graphics2D g) {
|
||||
int scale = getScale();
|
||||
if (path != null) {
|
||||
if (background != null) {
|
||||
g.setColor(background);
|
||||
g.fill(path);
|
||||
}
|
||||
if (parent != null && parent.background == background && gradient != null) {
|
||||
int left = getGap(this, scale);
|
||||
int right = getGap(null, scale);
|
||||
Path2D path = new Path2D.Double();
|
||||
path.moveTo(bounds.x - left, bounds.y);
|
||||
path.lineTo(bounds.x - left + right, bounds.y + bounds.height * 0.5);
|
||||
path.lineTo(bounds.x - left, bounds.y + bounds.height);
|
||||
g.setPaint(gradient);
|
||||
g.draw(path); //TODO: paint >
|
||||
}
|
||||
}
|
||||
if (effectType == EffectType.ROUNDED_BOX) {
|
||||
Rectangle bounds = getBounds(scale, scale);
|
||||
RectanglePainter.paint(g, bounds.x, bounds.y, bounds.width, bounds.height, bounds.height / 2, background, effectColor);
|
||||
}
|
||||
else if (effectType == EffectType.BOXED) {
|
||||
Rectangle bounds = getBounds(scale, scale);
|
||||
RectanglePainter.paint(g, bounds.x, bounds.y, bounds.width, bounds.height, 0, background, effectColor);
|
||||
}
|
||||
else if (background != null) {
|
||||
g.setColor(background);
|
||||
g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
|
||||
if (isSelected(crumb)) {
|
||||
paintMarker(g, bounds.x, bounds.y, bounds.width, bounds.height, crumb, scale * 2);
|
||||
}
|
||||
}
|
||||
else if (isSelected(crumb)) {
|
||||
paintMarker(g, bounds.x, bounds.y, bounds.width, bounds.height, crumb, scale * 2);
|
||||
}
|
||||
else if (isHovered(crumb)) {
|
||||
paintMarker(g, bounds.x, bounds.y, bounds.width, bounds.height, crumb, scale);
|
||||
}
|
||||
if (font != null) {
|
||||
g.setFont(font);
|
||||
FontMetrics fm = g.getFontMetrics(font);
|
||||
if (fm != null) {
|
||||
Rectangle iconR = new Rectangle();
|
||||
Rectangle textR = new Rectangle();
|
||||
Rectangle viewR = getBounds(getLeftRight(scale), getTopBottom(scale));
|
||||
String text = layout(fm, iconR, textR, viewR);
|
||||
if (!StringUtil.isEmpty(text)) {
|
||||
g.setColor(foreground != null ? foreground : getForeground());
|
||||
g.drawString(text, textR.x, textR.y += fm.getAscent());
|
||||
if (effectColor != null && effectType != null) {
|
||||
EffectPainter painter = getEffectPainter(effectType);
|
||||
if (painter != null) {
|
||||
g.setColor(effectColor);
|
||||
textR.height = painter == EffectPainter.STRIKE_THROUGH ? fm.getAscent() : fm.getDescent();
|
||||
painter.paint(g, textR.x, textR.y, textR.width, textR.height, font);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (icon != null) {
|
||||
icon.paintIcon(Breadcrumbs.this, g, iconR.x, iconR.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.ui.components.breadcrumbs;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import javax.swing.JLabel;
|
||||
|
||||
/**
|
||||
* @author Sergey.Malenkov
|
||||
*/
|
||||
final class CrumbLabel extends JLabel {
|
||||
Crumb crumb;
|
||||
|
||||
void setCrumb(Crumb crumb) {
|
||||
this.crumb = crumb;
|
||||
if (crumb == null) {
|
||||
setVisible(false);
|
||||
setBounds(0, 0, 0, 0);
|
||||
}
|
||||
else {
|
||||
setVisible(true);
|
||||
setIcon(crumb.getIcon());
|
||||
setText(crumb.getText());
|
||||
setToolTipText(crumb.getTooltip());
|
||||
}
|
||||
}
|
||||
|
||||
private Breadcrumbs getBreadcrumbs() {
|
||||
Component component = crumb == null ? null : getParent();
|
||||
return component instanceof Breadcrumbs ? (Breadcrumbs)component : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
Breadcrumbs breadcrumbs = getBreadcrumbs();
|
||||
if (breadcrumbs != null && g instanceof Graphics2D) {
|
||||
breadcrumbs.paint((Graphics2D)g, 0, 0, getWidth(), getHeight(), crumb);
|
||||
}
|
||||
super.paintComponent(g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Font getFont() {
|
||||
Breadcrumbs breadcrumbs = getBreadcrumbs();
|
||||
if (breadcrumbs != null) {
|
||||
Font font = breadcrumbs.getFont(crumb);
|
||||
if (font != null) return font;
|
||||
}
|
||||
return super.getFont();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getForeground() {
|
||||
Breadcrumbs breadcrumbs = getBreadcrumbs();
|
||||
return breadcrumbs != null
|
||||
? breadcrumbs.getForeground(crumb)
|
||||
: super.getForeground();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFont(Font font) {
|
||||
// do not revalidate the label
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForeground(Color color) {
|
||||
// do not revalidate the label
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackground(Color color) {
|
||||
// do not revalidate the label
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOpaque(boolean isOpaque) {
|
||||
// do not paint background
|
||||
}
|
||||
}
|
||||
@@ -1042,12 +1042,13 @@
|
||||
<option name="BREADCRUMBS_HOVERED">
|
||||
<value>
|
||||
<option name="FOREGROUND" value="585858"/>
|
||||
<option name="BACKGROUND" value="dedede"/>
|
||||
<option name="BACKGROUND" value="e0e0e0"/>
|
||||
</value>
|
||||
</option>
|
||||
<option name="BREADCRUMBS_CURRENT">
|
||||
<value>
|
||||
<option name="FOREGROUND" value="585858"/>
|
||||
<option name="BACKGROUND" value="d6d6d6"/>
|
||||
</value>
|
||||
</option>
|
||||
<option name="BREADCRUMBS_INACTIVE">
|
||||
@@ -2146,12 +2147,13 @@
|
||||
<option name="BREADCRUMBS_HOVERED">
|
||||
<value>
|
||||
<option name="FOREGROUND" value="bbbbbb"/>
|
||||
<option name="BACKGROUND" value="44474a"/>
|
||||
<option name="BACKGROUND" value="3a3c3e"/>
|
||||
</value>
|
||||
</option>
|
||||
<option name="BREADCRUMBS_CURRENT">
|
||||
<value>
|
||||
<option name="FOREGROUND" value="bbbbbb"/>
|
||||
<option name="BACKGROUND" value="44474a"/>
|
||||
</value>
|
||||
</option>
|
||||
<option name="BREADCRUMBS_INACTIVE">
|
||||
|
||||
@@ -949,7 +949,7 @@ editor.breadcrumbs.small.font.description=Use small font to show breadcrumbs
|
||||
editor.breadcrumbs.system.font=false
|
||||
editor.breadcrumbs.system.font.description=Use system font to show breadcrumbs
|
||||
|
||||
editor.breadcrumbs.marker=true
|
||||
editor.breadcrumbs.marker=false
|
||||
editor.breadcrumbs.marker.description=Use a special line to mark a hovered or current breadcrumbs
|
||||
|
||||
editor.breadcrumbs.marker.alpha=0.6
|
||||
|
||||
@@ -41,8 +41,8 @@ final class PsiBreadcrumbs extends Breadcrumbs {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paint(Graphics2D g, int x, int y, int width, int height, Crumb crumb, int thickness) {
|
||||
super.paint(g, x, y, width, above ? height : thickness, crumb, thickness);
|
||||
protected void paintMarker(Graphics2D g, int x, int y, int width, int height, Crumb crumb, int thickness) {
|
||||
super.paintMarker(g, x, y, width, above ? height : thickness, crumb, thickness);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,7 +51,7 @@ final class PsiBreadcrumbs extends Breadcrumbs {
|
||||
float size = font.getSize2D();
|
||||
if (Registry.is("editor.breadcrumbs.system.font")) {
|
||||
Font system = UIUtil.getLabelFont();
|
||||
if (system != null) font = system;
|
||||
if (system != null) font = system.deriveFont(size);
|
||||
}
|
||||
if (Registry.is("editor.breadcrumbs.small.font")) {
|
||||
font = font.deriveFont(size / 1.09f);
|
||||
|
||||
Reference in New Issue
Block a user