From 178a73cfe5241d39019f981d57ce46328137dd9d Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Mon, 13 Oct 2014 18:21:36 +0400 Subject: [PATCH] Appropriate borders, gaps and icons modified for different LaF to avoid ui shifts and movements between loading, edit_mode and normal state. --- .../intellij/dvcs/push/ui/LoadingIcon.java | 32 +--------- .../com/intellij/dvcs/push/ui/PushLog.java | 3 + .../intellij/dvcs/push/ui/RepositoryNode.java | 44 ++++++++++---- .../push/ui/RepositoryWithBranchPanel.java | 59 ++++++++++++++++++- .../dvcs/push/ui/SingleRepositoryNode.java | 12 ++-- 5 files changed, 101 insertions(+), 49 deletions(-) diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/LoadingIcon.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/LoadingIcon.java index 6d61c23a937e..0105032eba97 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/LoadingIcon.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/LoadingIcon.java @@ -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; diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java index f42698bc0de0..818e04a36426 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java @@ -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()); diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryNode.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryNode.java index 956806b61bbc..169d09a19c73 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryNode.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryNode.java @@ -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> 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 diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryWithBranchPanel.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryWithBranchPanel.java index ff4cddc833b4..833ae6aa6b4c 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryWithBranchPanel.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryWithBranchPanel.java @@ -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 extends NonOpaquePa private final JLabel myRepositoryLabel; private final ColoredTreeCellRenderer myTextRenderer; @NotNull private final List> 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 destPushTargetPanelComponent) { @@ -54,6 +61,7 @@ public class RepositoryWithBranchPanel 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 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 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 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; + } } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/SingleRepositoryNode.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/SingleRepositoryNode.java index 98c177b3326b..fccb004fd527 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/SingleRepositoryNode.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/SingleRepositoryNode.java @@ -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();