From 4fe7571aadb4b059954a13c42a14385feb3d15fd Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Fri, 29 Jan 2021 14:28:02 +0300 Subject: [PATCH] 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 --- .../diff-impl/intellij.platform.diff.impl.iml | 1 - .../tools/util/side/DiffContentPanel.java | 85 ++++++++++++++++--- .../src/com/intellij/diff/util/DiffUtil.java | 30 +++---- .../diff/util/SyncHeightComponent.java | 75 +++++++++------- .../diff/util/VerticalStackLayout.java | 52 ++++++++++++ 5 files changed, 184 insertions(+), 59 deletions(-) create mode 100644 platform/diff-impl/src/com/intellij/diff/util/VerticalStackLayout.java diff --git a/platform/diff-impl/intellij.platform.diff.impl.iml b/platform/diff-impl/intellij.platform.diff.impl.iml index bb81c3207db3..ad6cd7573d70 100644 --- a/platform/diff-impl/intellij.platform.diff.impl.iml +++ b/platform/diff-impl/intellij.platform.diff.impl.iml @@ -16,7 +16,6 @@ - diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/side/DiffContentPanel.java b/platform/diff-impl/src/com/intellij/diff/tools/util/side/DiffContentPanel.java index b9a29658547a..6b5accef623a 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/side/DiffContentPanel.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/side/DiffContentPanel.java @@ -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 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(); + } + } } diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java index 0cabdc3cf5c7..3257599f5622 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java @@ -565,21 +565,20 @@ public final class DiffUtil { private static JComponent createTitleWithNotifications(@Nullable DiffViewer viewer, @Nullable JComponent title, @NotNull DiffContent content) { - List notifications = new ArrayList<>(createCustomNotifications(viewer, content)); + List 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 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 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 components, int vGap) { + JPanel panel = new JPanel(new VerticalStackLayout(vGap)); + for (JComponent component : components) { + panel.add(component); } - return panel; } diff --git a/platform/diff-impl/src/com/intellij/diff/util/SyncHeightComponent.java b/platform/diff-impl/src/com/intellij/diff/util/SyncHeightComponent.java index 721dbaf78f94..0b47d6bdddd2 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/SyncHeightComponent.java +++ b/platform/diff-impl/src/com/intellij/diff/util/SyncHeightComponent.java @@ -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 myComponents; - SyncHeightComponent(@NotNull List components, int index) { - super(new BorderLayout()); - myComponents = components; - JComponent delegate = components.get(index); - if (delegate != null) add(delegate, BorderLayout.CENTER); - } + SyncHeightComponent(@NotNull List 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 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 mySyncComponents; + @Nullable private final JComponent myComponent; + + SyncHeightLayout(@NotNull List 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(); + } + } } diff --git a/platform/diff-impl/src/com/intellij/diff/util/VerticalStackLayout.java b/platform/diff-impl/src/com/intellij/diff/util/VerticalStackLayout.java new file mode 100644 index 000000000000..f814ab0d1c1a --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/util/VerticalStackLayout.java @@ -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(); + } +}