mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[ui] ability to add a second line to the status text
When a component is empty, it's nice to have an action displayed that can make it not empty. This action can be displayed as a link in the second line after the normal status text.
This commit is contained in:
committed by
Aleksey Pivovarov
parent
525621ccaa
commit
bf1e561431
@@ -35,17 +35,20 @@ public abstract class StatusText {
|
||||
public static final SimpleTextAttributes DEFAULT_ATTRIBUTES = SimpleTextAttributes.GRAYED_ATTRIBUTES;
|
||||
public static final String DEFAULT_EMPTY_TEXT = UIBundle.message("message.nothingToShow");
|
||||
|
||||
@Nullable
|
||||
private Component myOwner;
|
||||
private static final int Y_GAP = JBUI.scale(2);
|
||||
|
||||
@Nullable private Component myOwner;
|
||||
private Component myMouseTarget;
|
||||
private final MouseMotionListener myMouseMotionListener;
|
||||
private final ClickListener myClickListener;
|
||||
@NotNull private final MouseMotionListener myMouseMotionListener;
|
||||
@NotNull private final ClickListener myClickListener;
|
||||
|
||||
private boolean myIsDefaultText;
|
||||
|
||||
private String myText = "";
|
||||
protected final SimpleColoredComponent myComponent = new SimpleColoredComponent();
|
||||
@NotNull protected final SimpleColoredComponent myComponent = new SimpleColoredComponent();
|
||||
@NotNull private final SimpleColoredComponent mySecondaryComponent = new SimpleColoredComponent();
|
||||
private final List<ActionListener> myClickListeners = new ArrayList<>();
|
||||
private final List<ActionListener> mySecondaryListeners = new ArrayList<>();
|
||||
private boolean myHasActiveClickListeners; // calculated field for performance optimization
|
||||
private boolean myShowAboveCenter = true;
|
||||
|
||||
@@ -70,7 +73,6 @@ public abstract class StatusText {
|
||||
};
|
||||
|
||||
myMouseMotionListener = new MouseAdapter() {
|
||||
|
||||
private Cursor myOriginalCursor;
|
||||
|
||||
@Override
|
||||
@@ -94,6 +96,9 @@ public abstract class StatusText {
|
||||
myComponent.setFont(UIUtil.getLabelFont());
|
||||
setText(DEFAULT_EMPTY_TEXT, DEFAULT_ATTRIBUTES);
|
||||
myIsDefaultText = true;
|
||||
|
||||
mySecondaryComponent.setOpaque(false);
|
||||
mySecondaryComponent.setFont(UIUtil.getLabelFont());
|
||||
}
|
||||
|
||||
public void attachTo(@Nullable Component owner) {
|
||||
@@ -117,6 +122,25 @@ public abstract class StatusText {
|
||||
|
||||
protected abstract boolean isStatusVisible();
|
||||
|
||||
@Nullable
|
||||
private ActionListener findActionListenerAt(int xCoord, int yCoord) {
|
||||
if (myComponent.getPreferredSize().height >= yCoord) {
|
||||
return findListener(myComponent, myClickListeners, xCoord);
|
||||
}
|
||||
return findListener(mySecondaryComponent, mySecondaryListeners, xCoord);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ActionListener findListener(@NotNull SimpleColoredComponent component,
|
||||
@NotNull List<ActionListener> listeners,
|
||||
int xCoord) {
|
||||
int index = component.findFragmentAt(xCoord);
|
||||
if (index >= 0 && index < listeners.size()) {
|
||||
return listeners.get(index);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ActionListener findActionListenerAt(Point point) {
|
||||
if (!myHasActiveClickListeners || !isStatusVisible()) return null;
|
||||
@@ -125,10 +149,7 @@ public abstract class StatusText {
|
||||
|
||||
Rectangle b = getTextComponentBound();
|
||||
if (b.contains(point)) {
|
||||
int index = myComponent.findFragmentAt(point.x - b.x);
|
||||
if (index >= 0 && index < myClickListeners.size()) {
|
||||
return myClickListeners.get(index);
|
||||
}
|
||||
return findActionListenerAt(point.x - b.x, point.y - b.y);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -136,9 +157,9 @@ public abstract class StatusText {
|
||||
protected Rectangle getTextComponentBound() {
|
||||
Rectangle ownerRec = myOwner == null ? new Rectangle(0, 0, 0, 0) : myOwner.getBounds();
|
||||
|
||||
Dimension size = myComponent.getPreferredSize();
|
||||
Dimension size = getPreferredSize();
|
||||
int x = (ownerRec.width - size.width) / 2;
|
||||
int y = (ownerRec.height - size.height) / (isShowAboveCenter() ? 3 : 2);
|
||||
int y = (ownerRec.height - size.height) / (myShowAboveCenter ? 3 : 2);
|
||||
return new Rectangle(x, y, size.width, size.height);
|
||||
}
|
||||
|
||||
@@ -168,11 +189,17 @@ public abstract class StatusText {
|
||||
myText = "";
|
||||
myComponent.clear();
|
||||
myClickListeners.clear();
|
||||
mySecondaryComponent.clear();
|
||||
mySecondaryListeners.clear();
|
||||
myHasActiveClickListeners = false;
|
||||
if (myOwner != null && isStatusVisible()) myOwner.repaint();
|
||||
repaintOwner();
|
||||
return this;
|
||||
}
|
||||
|
||||
private void repaintOwner() {
|
||||
if (myOwner != null && isStatusVisible()) myOwner.repaint();
|
||||
}
|
||||
|
||||
public StatusText appendText(String text) {
|
||||
return appendText(text, DEFAULT_ATTRIBUTES);
|
||||
}
|
||||
@@ -193,7 +220,17 @@ public abstract class StatusText {
|
||||
if (listener != null) {
|
||||
myHasActiveClickListeners = true;
|
||||
}
|
||||
if (myOwner != null && isStatusVisible()) myOwner.repaint();
|
||||
repaintOwner();
|
||||
return this;
|
||||
}
|
||||
|
||||
public StatusText appendSecondaryText(String text, SimpleTextAttributes attrs, ActionListener listener) {
|
||||
mySecondaryComponent.append(text, attrs);
|
||||
mySecondaryListeners.add(listener);
|
||||
if (listener != null) {
|
||||
myHasActiveClickListeners = true;
|
||||
}
|
||||
repaintOwner();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -226,10 +263,23 @@ public abstract class StatusText {
|
||||
viewport.repaint(textBoundsInViewport);
|
||||
}
|
||||
|
||||
private void doPaintStatusText(Graphics g, Rectangle textComponentBounds) {
|
||||
myComponent.setBounds(0, 0, textComponentBounds.width, textComponentBounds.height);
|
||||
Graphics2D g2 = (Graphics2D)g.create(textComponentBounds.x, textComponentBounds.y, textComponentBounds.width, textComponentBounds.height);
|
||||
myComponent.paint(g2);
|
||||
private void doPaintStatusText(@NotNull Graphics g, @NotNull Rectangle bounds) {
|
||||
paintComponentAtY(myComponent, g, bounds, 0);
|
||||
if (hasSecondaryText()) paintComponentAtY(mySecondaryComponent, g, bounds, bounds.height / 2 + Y_GAP);
|
||||
}
|
||||
|
||||
private boolean hasSecondaryText() {
|
||||
return mySecondaryComponent.getCharSequence(false).length() > 0;
|
||||
}
|
||||
|
||||
private static void paintComponentAtY(@NotNull SimpleColoredComponent component, @NotNull Graphics g,
|
||||
@NotNull Rectangle bounds, int yCoord) {
|
||||
Dimension size = component.getPreferredSize();
|
||||
Graphics2D g2 = (Graphics2D)g.create(bounds.x + (bounds.width - size.width) / 2,
|
||||
bounds.y + yCoord,
|
||||
size.width, size.height);
|
||||
component.setBounds(0, 0, size.width, size.height);
|
||||
component.paint(g2);
|
||||
g2.dispose();
|
||||
}
|
||||
|
||||
@@ -239,6 +289,10 @@ public abstract class StatusText {
|
||||
}
|
||||
|
||||
public Dimension getPreferredSize() {
|
||||
return myComponent.getPreferredSize();
|
||||
Dimension componentSize = myComponent.getPreferredSize();
|
||||
if (!hasSecondaryText()) return componentSize;
|
||||
Dimension secondaryComponentSize = mySecondaryComponent.getPreferredSize();
|
||||
return new Dimension(Math.max(componentSize.width, secondaryComponentSize.width),
|
||||
componentSize.height + secondaryComponentSize.height + Y_GAP);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,8 +142,8 @@ class VcsLogChangesBrowser extends ChangesBrowserBase implements Disposable {
|
||||
}
|
||||
|
||||
if (myChanges.isEmpty() && details.getParents().size() > 1) {
|
||||
myViewer.getEmptyText().setText("No merged conflicts. ").
|
||||
appendText("Show changes to parents", getLinkAttributes(),
|
||||
myViewer.getEmptyText().setText("No merged conflicts.").
|
||||
appendSecondaryText("Show changes to parents", getLinkAttributes(),
|
||||
e -> myUiProperties.set(SHOW_CHANGES_FROM_PARENTS, true));
|
||||
}
|
||||
else {
|
||||
|
||||
Reference in New Issue
Block a user