Appropriate borders, gaps and icons modified for different LaF to avoid ui shifts and movements between loading, edit_mode and normal state.

This commit is contained in:
Nadya Zabrodina
2014-10-13 18:25:32 +04:00
parent f76e68c2e2
commit 178a73cfe5
5 changed files with 101 additions and 49 deletions
@@ -34,19 +34,8 @@ class LoadingIcon extends JBImageIcon {
private static final String LOADING_ICON = "/icons/loading.gif";
private static final Logger LOG = Logger.getInstance(LoadingIcon.class);
private final int myWidth;
private final int myHeight;
private final int myDeltaX;
private final int myDeltaY;
LoadingIcon(@NotNull Image image, int width, int height) {
LoadingIcon(@NotNull Image image) {
super(image);
int myOriginalWidth = image.getWidth(null);
int myOriginalHeight = image.getHeight(null);
myWidth = Math.max(width, myOriginalWidth);
myHeight = Math.max(height, myOriginalHeight);
myDeltaX = (myWidth - myOriginalWidth) / 2 - 2;
myDeltaY = (myHeight - myOriginalHeight) / 2 + 2;
}
@NotNull
@@ -56,33 +45,18 @@ class LoadingIcon extends JBImageIcon {
LOG.error("Couldn't load image: " + LOADING_ICON);
return createEmpty(width, height);
}
return new LoadingIcon(image, width, height);
return new LoadingIcon(image);
}
@NotNull
static LoadingIcon createEmpty(int width, int height) {
return new LoadingIcon(UIUtil.createImage(width, height, Transparency.TRANSLUCENT), width, height);
return new LoadingIcon(UIUtil.createImage(width, height, Transparency.TRANSLUCENT));
}
void setObserver(@NotNull JTree tree, @NotNull TreeNode treeNode) {
setImageObserver(new NodeImageObserver(tree, treeNode));
}
@Override
public final synchronized void paintIcon(final Component c, final Graphics g, final int x, final int y) {
super.paintIcon(c, g, x + myDeltaX, y + myDeltaY);
}
@Override
public int getIconHeight() {
return myHeight;
}
@Override
public int getIconWidth() {
return myWidth;
}
private static class NodeImageObserver implements ImageObserver {
@NotNull private final JTree myTree;
@NotNull private final DefaultTreeModel myModel;
@@ -355,6 +355,9 @@ public class PushLog extends JPanel implements TypeSafeDataProvider {
if (!(value instanceof DefaultMutableTreeNode)) {
return;
}
myCheckbox.setBorder(null); //checkBox may have no border by default, but insets are not null,
// it depends on LaF, OS and isItRenderedPane, see com.intellij.ide.ui.laf.darcula.ui.DarculaCheckBoxBorder.
// null border works as expected always.
if (value instanceof RepositoryNode) {
//todo simplify, remove instance of
myCheckbox.setVisible(((RepositoryNode)value).isCheckboxVisible());
@@ -27,7 +27,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@@ -40,7 +39,8 @@ public class RepositoryNode extends CheckedTreeNode implements EditableTreeNode,
@NotNull private final RepositoryWithBranchPanel myRepositoryPanel;
@Nullable private Future<AtomicReference<OutgoingResult>> myFuture;
protected final int myLoadingIconWidth;
protected final int myCheckBoxHGap;
private final int myCheckBoxVGap;
public RepositoryNode(@NotNull RepositoryWithBranchPanel repositoryPanel, @NotNull CheckBoxModel model, boolean enabled) {
super(repositoryPanel);
@@ -48,10 +48,9 @@ public class RepositoryNode extends CheckedTreeNode implements EditableTreeNode,
setChecked(false);
setEnabled(enabled);
myRepositoryPanel = repositoryPanel;
Dimension size = new JCheckBox().getPreferredSize();
myLoadingIconWidth = size.width;
myLoadingIcon = LoadingIcon.create(myLoadingIconWidth, size.height);
myLoadingIcon = myRepositoryPanel.getLoadingIcon();
myCheckBoxHGap = myRepositoryPanel.getLoadingIconAndCheckBoxGapH();
myCheckBoxVGap = myRepositoryPanel.getLoadingIconAndCheckBoxGapV();
}
@Override
@@ -71,25 +70,44 @@ public class RepositoryNode extends CheckedTreeNode implements EditableTreeNode,
@Override
public void render(@NotNull ColoredTreeCellRenderer renderer) {
int repoFixedWidth = 120;
int borderHOffset = myRepositoryPanel.getHBorderOffset(renderer);
int borderVOffset = myRepositoryPanel.getVBorderOffset(renderer);
if (myLoading.get()) {
renderer.setIcon(myLoadingIcon);
renderer.setIconOnTheRight(false);
repoFixedWidth += myLoadingIconWidth;
int checkBoxWidth = myRepositoryPanel.getCheckBoxWidth();
repoFixedWidth += checkBoxWidth;
if (myCheckBoxHGap > 0) {
renderer.append("");
renderer.appendFixedTextFragmentWidth(checkBoxWidth + renderer.getIconTextGap() + borderHOffset);
}
if (myCheckBoxVGap > 0) {
int shiftV = myCheckBoxVGap - borderVOffset;
renderer.setBorder(new EmptyBorder(shiftV / 2, 0, shiftV / 2, 0));
}
}
renderer.append(getRepoName(renderer, repoFixedWidth), SimpleTextAttributes.GRAY_ATTRIBUTES);
else {
if (myCheckBoxHGap <= 0) {
renderer.append("");
renderer.appendFixedTextFragmentWidth(myRepositoryPanel.calculateRendererShiftH(renderer));
}
if (myCheckBoxVGap <= 0) {
int shiftV = -myCheckBoxVGap + borderVOffset;
renderer.setBorder(new EmptyBorder(shiftV / 2, 0, shiftV / 2, 0));
}
}
renderer.append(getRepoName(renderer, repoFixedWidth), isChecked() ? SimpleTextAttributes.REGULAR_ATTRIBUTES : SimpleTextAttributes.GRAY_ATTRIBUTES);
renderer.appendFixedTextFragmentWidth(repoFixedWidth);
renderer.append(myRepositoryPanel.getSourceName(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
renderer.append(myRepositoryPanel.getArrow(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
renderer.append(myRepositoryPanel.getSourceName(), isChecked() ? SimpleTextAttributes.REGULAR_ATTRIBUTES : SimpleTextAttributes.GRAY_ATTRIBUTES);
renderer.append(myRepositoryPanel.getArrow(), isChecked() ? SimpleTextAttributes.REGULAR_ATTRIBUTES : SimpleTextAttributes.GRAY_ATTRIBUTES);
PushTargetPanel pushTargetPanel = myRepositoryPanel.getTargetPanel();
pushTargetPanel.render(renderer);
Insets insets = BorderFactory.createEmptyBorder().getBorderInsets(pushTargetPanel);
renderer.setBorder(new EmptyBorder(insets));
}
@NotNull
private String getRepoName(@NotNull ColoredTreeCellRenderer renderer, int maxWidth) {
String name = myRepositoryPanel.getRepositoryName();
return GraphicsUtil.stringWidth(name, renderer.getFont()) > maxWidth - UIUtil.DEFAULT_VGAP ? name + " " : name;
return GraphicsUtil.stringWidth(name, renderer.getFont()) > maxWidth - UIUtil.DEFAULT_HGAP ? name + " " : name;
}
@Override
@@ -23,6 +23,7 @@ import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.openapi.ui.popup.util.PopupUtil;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.SimpleColoredComponent;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
@@ -32,6 +33,7 @@ import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -46,6 +48,11 @@ public class RepositoryWithBranchPanel<T extends PushTarget> extends NonOpaquePa
private final JLabel myRepositoryLabel;
private final ColoredTreeCellRenderer myTextRenderer;
@NotNull private final List<RepositoryNodeListener<T>> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private final int myCheckBoxLoadingIconGapH;
private final int myCheckBoxLoadingIconGapV;
private final LoadingIcon myLoadingIcon;
private final int myCheckBoxWidth;
public RepositoryWithBranchPanel(@NotNull final Project project, @NotNull String repoName,
@NotNull String sourceName, @NotNull PushTargetPanel<T> destPushTargetPanelComponent) {
@@ -54,6 +61,7 @@ public class RepositoryWithBranchPanel<T extends PushTarget> extends NonOpaquePa
myRepositoryCheckbox = new JBCheckBox();
myRepositoryCheckbox.setFocusable(false);
myRepositoryCheckbox.setOpaque(false);
myRepositoryCheckbox.setBorder(null);
myRepositoryCheckbox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(@NotNull ActionEvent e) {
@@ -89,6 +97,14 @@ public class RepositoryWithBranchPanel<T extends PushTarget> extends NonOpaquePa
return error == null;
}
});
JCheckBox emptyBorderCheckBox = new JCheckBox();
emptyBorderCheckBox.setBorder(null);
Dimension size = emptyBorderCheckBox.getPreferredSize();
myCheckBoxWidth = size.width;
myLoadingIcon = LoadingIcon.create(myCheckBoxWidth, size.height);
myCheckBoxLoadingIconGapH = myCheckBoxWidth - myLoadingIcon.getIconWidth();
myCheckBoxLoadingIconGapV = size.height - myLoadingIcon.getIconHeight();
}
private void layoutComponents() {
@@ -122,17 +138,24 @@ public class RepositoryWithBranchPanel<T extends PushTarget> extends NonOpaquePa
boolean hasFocus) {
Rectangle bounds = tree.getPathBounds(tree.getPathForRow(row));
invalidate();
myTextRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
if (!(value instanceof SingleRepositoryNode)) {
RepositoryNode node = (RepositoryNode)value;
myRepositoryCheckbox.setSelected(node.isChecked());
myRepositoryCheckbox.setVisible(true);
myTextRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
if (myCheckBoxLoadingIconGapH < 0) {
myTextRenderer.append("");
myTextRenderer.appendFixedTextFragmentWidth(calculateRendererShiftH(myTextRenderer));
}
myTextRenderer.append(getRepositoryName(), SimpleTextAttributes.GRAY_ATTRIBUTES);
myTextRenderer.appendFixedTextFragmentWidth(120);
}
else {
myTextRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
SingleRepositoryNode singleRepositoryNode = ((SingleRepositoryNode)value);
myRepositoryCheckbox.setVisible(false);
myTextRenderer.setIcon(singleRepositoryNode.getEmptyIcon());
myTextRenderer.setIconOnTheRight(false);
myTextRenderer.append("");
}
myTextRenderer.append(getSourceName(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
myTextRenderer.append(getArrow(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
@@ -168,6 +191,38 @@ public class RepositoryWithBranchPanel<T extends PushTarget> extends NonOpaquePa
public PushTargetPanel getTargetPanel() {
return myDestPushTargetPanelComponent;
}
public LoadingIcon getLoadingIcon() {
return myLoadingIcon;
}
public int getCheckBoxWidth() {
return myCheckBoxWidth;
}
public int getLoadingIconAndCheckBoxGapH() {
return myCheckBoxLoadingIconGapH;
}
public int calculateRendererShiftH(@NotNull SimpleColoredComponent coloredRenderer) {
int borderOffset = getHBorderOffset(coloredRenderer);
return -myCheckBoxLoadingIconGapH + coloredRenderer.getIconTextGap() + coloredRenderer.getIpad().left + borderOffset;
}
public int getHBorderOffset(@NotNull SimpleColoredComponent coloredRenderer) {
Border border = coloredRenderer.getMyBorder();
return border != null ? border.getBorderInsets(coloredRenderer).left : 0;
}
public int getLoadingIconAndCheckBoxGapV() {
return myCheckBoxLoadingIconGapV;
}
public int getVBorderOffset(@NotNull SimpleColoredComponent coloredRenderer) {
Border border = coloredRenderer.getMyBorder();
return border != null ? border.getBorderInsets(coloredRenderer).top : 0;
}
}
@@ -28,7 +28,8 @@ public class SingleRepositoryNode extends RepositoryNode {
public SingleRepositoryNode(@NotNull RepositoryWithBranchPanel repositoryPanel, @NotNull CheckBoxModel model) {
super(repositoryPanel, model, true);
myRepositoryPanel = repositoryPanel;
myEmptyIcon = LoadingIcon.createEmpty(myLoadingIcon.getIconWidth(), myLoadingIcon.getIconHeight());
myEmptyIcon =
LoadingIcon.createEmpty(repositoryPanel.getLoadingIcon().getIconWidth(), repositoryPanel.getLoadingIcon().getIconHeight());
}
@Override
@@ -36,17 +37,18 @@ public class SingleRepositoryNode extends RepositoryNode {
return false;
}
public LoadingIcon getEmptyIcon() {
return myEmptyIcon;
}
@Override
public void fireOnSelectionChange(boolean isSelected) {
}
@Override
public void render(@NotNull ColoredTreeCellRenderer renderer) {
renderer.setIcon(myLoading.get() ? myLoadingIcon : myEmptyIcon);
renderer.setIcon(myLoading.get() ? myRepositoryPanel.getLoadingIcon() : myEmptyIcon);
renderer.setIconOnTheRight(false);
renderer.append("");
renderer.appendFixedTextFragmentWidth(myLoadingIconWidth);
renderer.append(myRepositoryPanel.getSourceName(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
renderer.append(myRepositoryPanel.getArrow(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
PushTargetPanel pushTargetPanel = myRepositoryPanel.getTargetPanel();