IDEA-239121 diff: fix title and notifications header layout

* Update layout to correctly handle gaps for invisible components (ex: hidden notification).
* Fix invisible notifications:
  EditorNotificationPanel has minimum height of 0, which was used by previous MigLayout
  to give it zero height and force multiple panels to draw on top of each other.

GitOrigin-RevId: 15b8741815b3ab6acc408289a4961d9ee778a1e7
This commit is contained in:
Aleksey Pivovarov
2021-03-24 12:41:57 +00:00
committed by intellij-monorepo-bot
parent f8da64a972
commit 4fe7571aad
5 changed files with 184 additions and 59 deletions
@@ -16,7 +16,6 @@
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
<orderEntry type="module" module-name="intellij.platform.lang" />
<orderEntry type="library" name="Guava" level="project" />
<orderEntry type="library" name="miglayout-swing" level="project" />
<orderEntry type="module" module-name="intellij.platform.statistics" />
<orderEntry type="module" module-name="intellij.platform.boot" />
<orderEntry type="module" module-name="intellij.platform.core.ui" />
@@ -21,13 +21,13 @@ import com.intellij.diff.util.DiffUtil;
import com.intellij.diff.util.InvisibleWrapper;
import com.intellij.ui.components.panels.Wrapper;
import com.intellij.util.containers.ContainerUtil;
import net.miginfocom.layout.CC;
import net.miginfocom.layout.LC;
import net.miginfocom.swing.MigLayout;
import com.intellij.util.ui.AbstractLayoutManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
class DiffContentPanel extends JPanel {
@@ -71,15 +71,11 @@ class DiffContentPanel extends JPanel {
@NotNull JComponent content,
@NotNull JComponent bottomBreadcrumbs) {
contentPanel.removeAll();
MigLayout mgr = new MigLayout(new LC().flowY().fill().hideMode(3)
.insets("0").gridGapY("0"));
contentPanel.setLayout(mgr);
contentPanel.add(title, new CC().growX().minWidth("0").gapY("0", String.valueOf(DiffUtil.TITLE_GAP)));
contentPanel.add(topBreadcrumbs, new CC().growX().minWidth("0"));
contentPanel.add(content, new CC().grow().push());
contentPanel.add(bottomBreadcrumbs, new CC().growX().minWidth("0"));
contentPanel.setLayout(new DiffContentLayout(title, topBreadcrumbs, content, bottomBreadcrumbs));
contentPanel.add(title);
contentPanel.add(topBreadcrumbs);
contentPanel.add(content);
contentPanel.add(bottomBreadcrumbs);
}
public static void syncTitleHeights(@NotNull List<DiffContentPanel> panels) {
@@ -96,4 +92,69 @@ class DiffContentPanel extends JPanel {
initLayout(contentPanel, title, topBreadcrumb, contentPanel.myContent, contentPanel.myBottomBreadcrumbs);
}
}
private static class DiffContentLayout extends AbstractLayoutManager {
@NotNull private final JComponent myTitle;
@NotNull private final JComponent myTopBreadcrumbs;
@NotNull private final JComponent myContent;
@NotNull private final JComponent myBottomBreadcrumbs;
DiffContentLayout(@NotNull JComponent title,
@NotNull JComponent topBreadcrumbs,
@NotNull JComponent content,
@NotNull JComponent bottomBreadcrumbs) {
myTitle = title;
myTopBreadcrumbs = topBreadcrumbs;
myContent = content;
myBottomBreadcrumbs = bottomBreadcrumbs;
}
@Override
public Dimension preferredLayoutSize(Container parent) {
int totalWidth = 0;
int totalHeight = 0;
for (JComponent component : Arrays.asList(myTitle, myTopBreadcrumbs, myContent, myBottomBreadcrumbs)) {
Dimension size = getPreferredSize(component);
totalWidth = Math.max(size.width, totalWidth);
totalHeight += size.height;
if (component == myTitle && size.height != 0) {
totalHeight += DiffUtil.TITLE_GAP;
}
}
return new Dimension(totalWidth, totalHeight);
}
@Override
public void layoutContainer(@NotNull Container parent) {
int y = 0;
int width = parent.getWidth();
int totalHeight = parent.getHeight();
Dimension titleSize = getPreferredSize(myTitle);
Dimension topSize = getPreferredSize(myTopBreadcrumbs);
Dimension bottomSize = getPreferredSize(myBottomBreadcrumbs);
int bottomY = totalHeight - bottomSize.height;
myTitle.setBounds(0, y, width, titleSize.height);
y += titleSize.height;
if (titleSize.height != 0) y += DiffUtil.TITLE_GAP;
myTopBreadcrumbs.setBounds(0, y, width, topSize.height);
y += topSize.height;
myContent.setBounds(0, y, width, Math.max(0, bottomY - y));
myBottomBreadcrumbs.setBounds(0, bottomY, width, bottomSize.height);
}
@NotNull
private static Dimension getPreferredSize(@NotNull JComponent component) {
return component.isVisible() ? component.getPreferredSize() : new Dimension();
}
}
}
@@ -565,21 +565,20 @@ public final class DiffUtil {
private static JComponent createTitleWithNotifications(@Nullable DiffViewer viewer,
@Nullable JComponent title,
@NotNull DiffContent content) {
List<JComponent> notifications = new ArrayList<>(createCustomNotifications(viewer, content));
List<JComponent> components = new ArrayList<>();
if (title != null) components.add(title);
components.addAll(createCustomNotifications(viewer, content));
if (content instanceof DocumentContent) {
Document document = ((DocumentContent)content).getDocument();
if (FileDocumentManager.getInstance().isPartialPreviewOfALargeFile(document)) {
notifications.add(DiffNotifications.createNotification(DiffBundle.message("error.file.is.too.large.only.preview.is.loaded")));
components.add(DiffNotifications.createNotification(DiffBundle.message("error.file.is.too.large.only.preview.is.loaded")));
}
}
if (notifications.isEmpty()) return title;
JPanel panel = new JPanel(new BorderLayout(0, TITLE_GAP));
if (title != null) panel.add(title, BorderLayout.NORTH);
panel.add(createStackedComponents(notifications, TITLE_GAP), BorderLayout.SOUTH);
return panel;
if (components.isEmpty()) return null;
return createStackedComponents(components, TITLE_GAP);
}
@Nullable
@@ -689,21 +688,18 @@ public final class DiffUtil {
if (!ContainerUtil.exists(components, Conditions.notNull())) return components;
List<JComponent> result = new ArrayList<>();
for (int i = 0; i < components.size(); i++) {
result.add(new SyncHeightComponent(components, i));
JComponent component = components.get(i);
result.add(new SyncHeightComponent(components, component));
}
return result;
}
@NotNull
public static JComponent createStackedComponents(@NotNull List<? extends JComponent> components, int gap) {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
for (int i = 0; i < components.size(); i++) {
if (i != 0) panel.add(Box.createVerticalStrut(JBUIScale.scale(gap)));
panel.add(components.get(i));
public static JComponent createStackedComponents(@NotNull List<? extends JComponent> components, int vGap) {
JPanel panel = new JPanel(new VerticalStackLayout(vGap));
for (JComponent component : components) {
panel.add(component);
}
return panel;
}
@@ -1,8 +1,9 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.diff.util;
import com.intellij.util.Function;
import com.intellij.util.ui.AbstractLayoutManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
@@ -11,35 +12,11 @@ import java.util.List;
public class SyncHeightComponent extends JPanel {
@NotNull private final List<? extends JComponent> myComponents;
SyncHeightComponent(@NotNull List<? extends JComponent> components, int index) {
super(new BorderLayout());
myComponents = components;
JComponent delegate = components.get(index);
if (delegate != null) add(delegate, BorderLayout.CENTER);
}
SyncHeightComponent(@NotNull List<? extends JComponent> syncComponents, @Nullable JComponent component) {
super(new SyncHeightLayout(syncComponents, component));
myComponents = syncComponents;
@Override
public Dimension getMinimumSize() {
Dimension size = super.getMinimumSize();
size.height = getMaximumHeight(JComponent::getPreferredSize);
return size;
}
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
size.height = getMaximumHeight(JComponent::getPreferredSize);
return size;
}
private int getMaximumHeight(@NotNull Function<? super JComponent, ? extends Dimension> getter) {
int height = 0;
for (JComponent component : myComponents) {
if (component != null) {
height = Math.max(height, getter.fun(component).height);
}
}
return height;
if (component != null) add(component);
}
public void revalidateAll() {
@@ -47,4 +24,44 @@ public class SyncHeightComponent extends JPanel {
if (component != null) component.revalidate();
}
}
private static class SyncHeightLayout extends AbstractLayoutManager {
@NotNull private final List<? extends JComponent> mySyncComponents;
@Nullable private final JComponent myComponent;
SyncHeightLayout(@NotNull List<? extends JComponent> syncComponents, @Nullable JComponent component) {
mySyncComponents = syncComponents;
myComponent = component;
}
@Override
public Dimension preferredLayoutSize(Container parent) {
int totalHeight = 0;
for (JComponent component : mySyncComponents) {
Dimension size = getPreferredSize(component);
totalHeight = Math.max(size.height, totalHeight);
}
int width = getPreferredSize(myComponent).width;
return new Dimension(width, totalHeight);
}
@Override
public void layoutContainer(@NotNull Container parent) {
int width = parent.getWidth();
int height = parent.getHeight();
if (myComponent == null) return;
Dimension size = getPreferredSize(myComponent);
myComponent.setBounds(0, 0, width, Math.min(height, size.height));
}
@NotNull
private static Dimension getPreferredSize(@Nullable Component component) {
return component != null && component.isVisible() ? component.getPreferredSize() : new Dimension();
}
}
}
@@ -0,0 +1,52 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.diff.util;
import com.intellij.util.ui.AbstractLayoutManager;
import com.intellij.util.ui.JBUI;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
class VerticalStackLayout extends AbstractLayoutManager {
private final int myVGap;
VerticalStackLayout(int vGap) {
myVGap = vGap;
}
@Override
public Dimension preferredLayoutSize(Container parent) {
int totalWidth = 0;
int totalHeight = 0;
for (Component component : parent.getComponents()) {
Dimension size = getPreferredSize(component);
totalWidth = Math.max(size.width, totalWidth);
if (size.height != 0 && totalHeight != 0) totalHeight += myVGap;
totalHeight += size.height;
}
return new Dimension(totalWidth, totalHeight);
}
@Override
public void layoutContainer(@NotNull Container parent) {
int width = parent.getWidth();
int y = 0;
for (Component component : parent.getComponents()) {
Dimension size = getPreferredSize(component);
component.setBounds(0, y, width, size.height);
if (size.height != 0) y += myVGap;
y += size.height;
}
}
@NotNull
private static Dimension getPreferredSize(@NotNull Component component) {
return component.isVisible() ? component.getPreferredSize() : new Dimension();
}
}