/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java
https://bitbucket.org/nbargnesi/idea · Java · 491 lines · 389 code · 87 blank · 15 comment · 125 complexity · 75aaf6051ab2f9b1fac8563eb7f5eeed MD5 · raw file
- /*
- * Copyright 2000-2012 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.tabs.impl.singleRow;
- import com.intellij.openapi.util.text.StringUtil;
- import com.intellij.ui.tabs.JBTabsPosition;
- import com.intellij.ui.tabs.TabInfo;
- import com.intellij.ui.tabs.TabsUtil;
- import com.intellij.ui.tabs.impl.*;
- import org.jetbrains.annotations.Nullable;
- import javax.swing.*;
- import java.awt.*;
- import java.awt.event.MouseAdapter;
- import java.awt.event.MouseEvent;
- import java.util.Collections;
- import java.util.Comparator;
- import java.util.List;
- public class SingleRowLayout extends TabLayout {
- final JBTabsImpl myTabs;
- public SingleRowPassInfo myLastSingRowLayout;
- private final SingleRowLayoutStrategy myTop;
- private final SingleRowLayoutStrategy myLeft;
- private final SingleRowLayoutStrategy myBottom;
- private final SingleRowLayoutStrategy myRight;
- public final MoreTabsIcon myMoreIcon = new MoreTabsIcon() {
- @Nullable
- protected Rectangle getIconRec() {
- return myLastSingRowLayout != null ? myLastSingRowLayout.moreRect : null;
- }
- @Override
- protected int getIconY(Rectangle iconRec) {
- return super.getIconY(iconRec) +
- (myTabs.getTabsPosition() == JBTabsPosition.bottom
- ? TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT
- : -(TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT / 2));
- }
- };
- public JPopupMenu myMorePopup;
- public final GhostComponent myLeftGhost = new GhostComponent(RowDropPolicy.first, RowDropPolicy.first);
- public final GhostComponent myRightGhost = new GhostComponent(RowDropPolicy.last, RowDropPolicy.first);
- private enum RowDropPolicy {
- first, last
- }
- private RowDropPolicy myRowDropPolicy = RowDropPolicy.first;
- @Override
- public boolean isSideComponentOnTabs() {
- return getStrategy().isSideComponentOnTabs();
- }
- @Override
- public ShapeTransform createShapeTransform(Rectangle labelRec) {
- return getStrategy().createShapeTransform(labelRec);
- }
- @Override
- public boolean isDragOut(TabLabel tabLabel, int deltaX, int deltaY) {
- return getStrategy().isDragOut(tabLabel, deltaX, deltaY);
- }
- public SingleRowLayout(final JBTabsImpl tabs) {
- myTabs = tabs;
- myTop = new SingleRowLayoutStrategy.Top(this);
- myLeft = new SingleRowLayoutStrategy.Left(this);
- myBottom = new SingleRowLayoutStrategy.Bottom(this);
- myRight = new SingleRowLayoutStrategy.Right(this);
- }
- SingleRowLayoutStrategy getStrategy() {
- switch (myTabs.getPresentation().getTabsPosition()) {
- case top:
- return myTop;
- case left:
- return myLeft;
- case bottom:
- return myBottom;
- case right:
- return myRight;
- }
- return null;
- }
- protected boolean checkLayoutLabels(SingleRowPassInfo data) {
- boolean layoutLabels = true;
- if (!myTabs.myForcedRelayout &&
- myLastSingRowLayout != null &&
- myLastSingRowLayout.contentCount == myTabs.getTabCount() &&
- myLastSingRowLayout.layoutSize.equals(myTabs.getSize()) &&
- myLastSingRowLayout.scrollOffset == getScrollOffset()) {
- for (TabInfo each : data.myVisibleInfos) {
- final TabLabel eachLabel = myTabs.myInfo2Label.get(each);
- if (!eachLabel.isValid()) {
- break;
- }
- if (myTabs.getSelectedInfo() == each) {
- if (eachLabel.getBounds().width != 0) {
- layoutLabels = false;
- }
- }
- }
- }
- return layoutLabels;
- }
- int getScrollOffset() {
- return 0;
- }
- public void scroll(int units) {
- }
- public int getScrollUnitIncrement() {
- return 0;
- }
- public void scrollSelectionInView() {
- }
- public LayoutPassInfo layoutSingleRow(List<TabInfo> visibleInfos) {
- if (JBEditorTabs.isAlphabeticalMode()) {
- Collections.sort(visibleInfos, new Comparator<TabInfo>() {
- @Override
- public int compare(TabInfo o1, TabInfo o2) {
- return StringUtil.naturalCompare(o1.getText(), o2.getText());
- }
- });
- }
- SingleRowPassInfo data = new SingleRowPassInfo(this, visibleInfos);
- final boolean layoutLabels = checkLayoutLabels(data);
- if (!layoutLabels) {
- data = myLastSingRowLayout;
- }
- final TabInfo selected = myTabs.getSelectedInfo();
- prepareLayoutPassInfo(data, selected);
- myTabs.resetLayout(layoutLabels || myTabs.isHideTabs());
- if (layoutLabels && !myTabs.isHideTabs()) {
- data.position = getStrategy().getStartPosition(data) - getScrollOffset();
- recomputeToLayout(data);
- layoutLabelsAndGhosts(data);
- layoutMoreButton(data);
- }
- if (selected != null) {
- data.comp = selected.getComponent();
- getStrategy().layoutComp(data);
- }
- updateMoreIconVisibility(data);
- data.tabRectangle = new Rectangle();
- if (data.toLayout.size() > 0) {
- final TabLabel firstLabel = myTabs.myInfo2Label.get(data.toLayout.get(0));
- final TabLabel lastLabel = findLastVisibleLabel(data);
- if (firstLabel != null && lastLabel != null) {
- data.tabRectangle.x = firstLabel.getBounds().x;
- data.tabRectangle.y = firstLabel.getBounds().y;
- data.tabRectangle.width = (int)lastLabel.getBounds().getMaxX() - data.tabRectangle.x;
- data.tabRectangle.height = (int)lastLabel.getBounds().getMaxY() - data.tabRectangle.y;
- }
- }
- myLastSingRowLayout = data;
- return data;
- }
- @Nullable
- protected TabLabel findLastVisibleLabel(SingleRowPassInfo data) {
- return myTabs.myInfo2Label.get(data.toLayout.get(data.toLayout.size() - 1));
- }
- protected void prepareLayoutPassInfo(SingleRowPassInfo data, TabInfo selected) {
- data.insets = myTabs.getLayoutInsets();
- final JBTabsImpl.Toolbar selectedToolbar = myTabs.myInfo2Toolbar.get(selected);
- data.hToolbar = selectedToolbar != null && myTabs.myHorizontalSide && !selectedToolbar.isEmpty() ? selectedToolbar : null;
- data.vToolbar = selectedToolbar != null && !myTabs.myHorizontalSide && !selectedToolbar.isEmpty() ? selectedToolbar : null;
- data.toFitLength = getStrategy().getToFitLength(data);
- if (myTabs.isGhostsAlwaysVisible()) {
- data.toFitLength -= JBTabsImpl.getGhostTabLength() * 2 + (JBTabsImpl.getInterTabSpaceLength() * 2);
- }
- }
- protected void updateMoreIconVisibility(SingleRowPassInfo data) {
- myMoreIcon.setPainted(data.toLayout.size() > 0 && data.myVisibleInfos.size() > data.toLayout.size());
- }
- protected void layoutMoreButton(SingleRowPassInfo data) {
- if (data.toDrop.size() > 0) {
- data.moreRect = getStrategy().getMoreRect(data);
- }
- }
- private void layoutLabelsAndGhosts(final SingleRowPassInfo data) {
- if (data.firstGhostVisible || myTabs.isGhostsAlwaysVisible()) {
- data.firstGhost = getStrategy().getLayoutRect(data, data.position, JBTabsImpl.getGhostTabLength());
- myTabs.layout(myLeftGhost, data.firstGhost);
- data.position += getStrategy().getLengthIncrement(data.firstGhost.getSize()) + JBTabsImpl.getInterTabSpaceLength();
- }
- int deltaToFit = 0;
- if (data.firstGhostVisible || data.lastGhostVisible) {
- if (data.requiredLength < data.toFitLength && getStrategy().canBeStretched()) {
- deltaToFit = (int)Math.floor((data.toFitLength - data.requiredLength) / (double)data.toLayout.size());
- }
- }
- int totalLength = 0;
- int positionStart = data.position;
- boolean layoutStopped = false;
- for (TabInfo eachInfo : data.toLayout) {
- final TabLabel label = myTabs.myInfo2Label.get(eachInfo);
- if (layoutStopped) {
- label.setActionPanelVisible(false);
- final Rectangle rec = getStrategy().getLayoutRect(data, 0, 0);
- myTabs.layout(label, rec);
- continue;
- }
- label.setActionPanelVisible(true);
- final Dimension eachSize = label.getPreferredSize();
- boolean isLast = data.toLayout.indexOf(eachInfo) == data.toLayout.size() - 1;
- int length;
- if (!isLast || deltaToFit == 0) {
- length = getStrategy().getLengthIncrement(eachSize) + deltaToFit;
- }
- else {
- length = data.toFitLength - totalLength;
- }
- boolean continueLayout = applyTabLayout(data, label, length, deltaToFit);
- data.position = getStrategy().getMaxPosition(label.getBounds());
- data.position += JBTabsImpl.getInterTabSpaceLength();
- totalLength = getStrategy().getMaxPosition(label.getBounds()) - positionStart + JBTabsImpl.getInterTabSpaceLength();
- if (!continueLayout) {
- layoutStopped = true;
- }
- }
- for (TabInfo eachInfo : data.toDrop) {
- JBTabsImpl.resetLayout(myTabs.myInfo2Label.get(eachInfo));
- }
- if (data.lastGhostVisible || myTabs.isGhostsAlwaysVisible()) {
- data.lastGhost = getStrategy().getLayoutRect(data, data.position, JBTabsImpl.getGhostTabLength());
- myTabs.layout(myRightGhost, data.lastGhost);
- }
- }
- protected boolean applyTabLayout(SingleRowPassInfo data, TabLabel label, int length, int deltaToFit) {
- final Rectangle rec = getStrategy().getLayoutRect(data, data.position, length);
- myTabs.layout(label, rec);
- label.setAlignmentToCenter((deltaToFit > 0 || myTabs.isEditorTabs()) && getStrategy().isToCenterTextWhenStretched());
- return true;
- }
- protected void recomputeToLayout(final SingleRowPassInfo data) {
- calculateRequiredLength(data);
- while (true) {
- if (data.requiredLength <= data.toFitLength - data.position) break;
- if (data.toLayout.size() == 0) break;
- final TabInfo first = data.toLayout.get(0);
- final TabInfo last = data.toLayout.get(data.toLayout.size() - 1);
- if (myRowDropPolicy == RowDropPolicy.first) {
- if (first != myTabs.getSelectedInfo()) {
- processDrop(data, first, true);
- }
- else if (last != myTabs.getSelectedInfo()) {
- processDrop(data, last, false);
- }
- else {
- break;
- }
- }
- else {
- if (last != myTabs.getSelectedInfo()) {
- processDrop(data, last, false);
- }
- else if (first != myTabs.getSelectedInfo()) {
- processDrop(data, first, true);
- }
- else {
- break;
- }
- }
- }
- for (int i = 1; i < data.myVisibleInfos.size() - 1; i++) {
- final TabInfo each = data.myVisibleInfos.get(i);
- final TabInfo prev = data.myVisibleInfos.get(i - 1);
- final TabInfo next = data.myVisibleInfos.get(i + 1);
- if (data.toLayout.contains(each) && data.toDrop.contains(prev)) {
- myLeftGhost.setInfo(prev);
- }
- else if (data.toLayout.contains(each) && data.toDrop.contains(next)) {
- myRightGhost.setInfo(next);
- }
- }
- }
- protected void calculateRequiredLength(SingleRowPassInfo data) {
- for (TabInfo eachInfo : data.myVisibleInfos) {
- data.requiredLength += getRequiredLength(eachInfo);
- data.toLayout.add(eachInfo);
- }
- }
- protected int getRequiredLength(TabInfo eachInfo) {
- return getStrategy().getLengthIncrement(myTabs.myInfo2Label.get(eachInfo).getPreferredSize())
- + (myTabs.isEditorTabs() ? JBTabsImpl.getInterTabSpaceLength() : 0);
- }
- public boolean isTabHidden(TabInfo tabInfo) {
- return myLastSingRowLayout.toDrop.contains(tabInfo);
- }
- public class GhostComponent extends JLabel {
- private TabInfo myInfo;
- private GhostComponent(final RowDropPolicy before, final RowDropPolicy after) {
- addMouseListener(new MouseAdapter() {
- public void mousePressed(final MouseEvent e) {
- if (JBTabsImpl.isSelectionClick(e, true) && myInfo != null) {
- myRowDropPolicy = before;
- myTabs.select(myInfo, true).doWhenDone(new Runnable() {
- public void run() {
- myRowDropPolicy = after;
- }
- });
- } else {
- MouseEvent event = SwingUtilities.convertMouseEvent(e.getComponent(), e, myTabs);
- myTabs.processMouseEvent(event);
- }
- }
- });
- }
- public void setInfo(@Nullable final TabInfo info) {
- myInfo = info;
- setToolTipText(info != null ? info.getTooltipText() : null);
- }
- public void reset() {
- JBTabsImpl.resetLayout(this);
- setInfo(null);
- }
- }
- private void processDrop(final SingleRowPassInfo data, final TabInfo info, boolean isFirstSide) {
- data.requiredLength -= getStrategy().getLengthIncrement(myTabs.myInfo2Label.get(info).getPreferredSize());
- data.toDrop.add(info);
- data.toLayout.remove(info);
- if (data.toDrop.size() == 1) {
- data.toFitLength -= data.moreRectAxisSize;
- }
- if (!data.firstGhostVisible && isFirstSide) {
- data.firstGhostVisible = !myTabs.isEditorTabs();
- if (!myTabs.isGhostsAlwaysVisible() && !myTabs.isEditorTabs()) {
- data.toFitLength -= JBTabsImpl.getGhostTabLength();
- }
- }
- else if (!data.lastGhostVisible && !isFirstSide) {
- data.lastGhostVisible = !myTabs.isEditorTabs();
- if (!myTabs.isGhostsAlwaysVisible() && !myTabs.isEditorTabs()) {
- data.toFitLength -= JBTabsImpl.getGhostTabLength();
- }
- }
- }
- @Override
- public int getDropIndexFor(Point point) {
- if (myLastSingRowLayout == null) return -1;
- int result = -1;
- Component c = myTabs.getComponentAt(point);
- if (c instanceof JBTabsImpl) {
- for (int i = 0; i < myLastSingRowLayout.myVisibleInfos.size() - 1; i++) {
- TabLabel first = myTabs.myInfo2Label.get(myLastSingRowLayout.myVisibleInfos.get(i));
- TabLabel second = myTabs.myInfo2Label.get(myLastSingRowLayout.myVisibleInfos.get(i + 1));
- Rectangle firstBounds = first.getBounds();
- Rectangle secondBounds = second.getBounds();
- final boolean between;
- boolean horizontal = getStrategy() instanceof SingleRowLayoutStrategy.Horizontal;
- if (horizontal) {
- between = firstBounds.getMaxX() < point.x
- && secondBounds.getX() > point.x
- && firstBounds.y < point.y
- && secondBounds.getMaxY() > point.y;
- } else {
- between = firstBounds.getMaxY() < point.y
- && secondBounds.getY() > point.y
- && firstBounds.x < point.x
- && secondBounds.getMaxX() > point.x;
- }
- if (between) {
- c = first;
- break;
- }
- }
- }
- if (c instanceof TabLabel) {
- TabInfo info = ((TabLabel)c).getInfo();
- int index = myLastSingRowLayout.myVisibleInfos.indexOf(info);
- boolean isDropTarget = myTabs.isDropTarget(info);
- if (!isDropTarget) {
- for (int i = 0; i <= index; i++) {
- if (myTabs.isDropTarget(myLastSingRowLayout.myVisibleInfos.get(i))) {
- index -= 1;
- break;
- }
- }
- result = index;
- } else if (index < myLastSingRowLayout.myVisibleInfos.size()) {
- result = index;
- }
- } else if (c instanceof GhostComponent) {
- GhostComponent ghost = (GhostComponent)c;
- TabInfo info = ghost.myInfo;
- if (info != null) {
- int index = myLastSingRowLayout.myVisibleInfos.indexOf(info);
- index += myLeftGhost == ghost ? -1 : 1;
- result = index >= 0 && index < myLastSingRowLayout.myVisibleInfos.size() ? index : -1;
- } else {
- if (myLastSingRowLayout.myVisibleInfos.size() == 0) {
- result = 0;
- } else {
- result = myLeftGhost == ghost ? 0 : myLastSingRowLayout.myVisibleInfos.size() - 1;
- }
- }
- }
- return result;
- }
- }