All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.intellij.ui.BalloonLayoutImpl Maven / Gradle / Ivy

/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.ui;

import com.intellij.openapi.Disposable;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.wm.impl.ToolWindowsPane;
import com.intellij.util.Alarm;
import com.intellij.util.ui.JBInsets;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.ArrayList;
import java.util.List;

public class BalloonLayoutImpl implements BalloonLayout {

  private final JLayeredPane myLayeredPane;
  private final Insets myInsets;

  private final List myBalloons = new ArrayList();

  private final Alarm myRelayoutAlarm = new Alarm();
  private final Runnable myRelayoutRunnable = new Runnable() {
    public void run() {
      relayout();
    }
  };
  private final JRootPane myParent;

  public BalloonLayoutImpl(@NotNull JRootPane parent, @NotNull Insets insets) {
    myParent = parent;
    myLayeredPane = parent.getLayeredPane();
    myInsets = insets;
    myLayeredPane.addComponentListener(new ComponentAdapter() {
      @Override
      public void componentResized(@NotNull ComponentEvent e) {
        queueRelayout();
      }
    });
  }

  @Override
  public void add(final Balloon balloon) {
    myBalloons.add(balloon);
    Disposer.register(balloon, new Disposable() {
      public void dispose() {
        myBalloons.remove(balloon);
        queueRelayout();
      }
    });

    relayout();
    balloon.show(myLayeredPane);
  }


  public void queueRelayout() {
    myRelayoutAlarm.cancelAllRequests();
    myRelayoutAlarm.addRequest(myRelayoutRunnable, 200);
  }

  private void relayout() {
    final Dimension size = myLayeredPane.getSize();

    JBInsets.removeFrom(size, myInsets);

    final Rectangle layoutRec = new Rectangle(new Point(myInsets.left, myInsets.top), size);

    List> columns = createColumns(layoutRec);
    while (columns.size() > 1) {
      myBalloons.remove(0);
      columns = createColumns(layoutRec);
    }
    List columnWidths = computeWidths(columns);

    ToolWindowsPane pane = UIUtil.findComponentOfType(myParent, ToolWindowsPane.class);
    JComponent component = pane != null ? pane : myParent;
    int paneOnScreen = component.isShowing() ? component.getLocationOnScreen().y : 0;
    int layerOnScreen = myLayeredPane.isShowing() ? myLayeredPane.getLocationOnScreen().y : 0;
    int toolbarsOffset = paneOnScreen - layerOnScreen;
    
    JComponent layeredPane = pane != null ? pane.getMyLayeredPane() : null;
    int eachColumnX = (layeredPane == null ? myLayeredPane.getWidth() : layeredPane.getX() + layeredPane.getWidth()) - 4; 
    
    for (int i = 0; i < columns.size(); i++) {
      final ArrayList eachColumn = columns.get(i);
      final Integer eachWidth = columnWidths.get(i);
      eachColumnX -= eachWidth.intValue();
      int eachY = toolbarsOffset + 2;
      for (Balloon eachBalloon : eachColumn) {
        final Rectangle eachRec = new Rectangle();
        final Dimension eachPrefSize = eachBalloon.getPreferredSize();
        eachRec.setSize(eachPrefSize);
        if (((BalloonImpl)eachBalloon).hasShadow()) {
          final int shadowSize = ((BalloonImpl)eachBalloon).getShadowBorderSize();
          eachRec.width += 2 * shadowSize;
          eachRec.height += 2 * shadowSize;
        }
        eachY += 2; //space between two notifications
        eachRec.setLocation(eachColumnX + eachWidth.intValue() - eachRec.width, eachY);
        eachBalloon.setBounds(eachRec);
        eachY += eachRec.height;
      }
    }
  }

  private static List computeWidths(List> columns) {
    List columnWidths = new ArrayList();
    for (ArrayList eachColumn : columns) {
      int maxWidth = 0;
      for (Balloon each : eachColumn) {
        maxWidth = Math.max(each.getPreferredSize().width, maxWidth);
      }
      columnWidths.add(maxWidth);
    }
    return columnWidths;
  }

  private List> createColumns(Rectangle layoutRec) {
    List> columns = new ArrayList>();

    ArrayList eachColumn = new ArrayList();
    columns.add(eachColumn);

    int eachColumnHeight = 0;
    for (Balloon each : myBalloons) {
      final Dimension eachSize = each.getPreferredSize();
      if (eachColumnHeight + eachSize.height > layoutRec.getHeight()) {
        eachColumn = new ArrayList();
        columns.add(eachColumn);
        eachColumnHeight = 0;
      }
      eachColumn.add(each);
      eachColumnHeight += eachSize.height;
    }
    return columns;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy