From 8ede9e40b44c9a04568ff303f90cb68f88d72a05 Mon Sep 17 00:00:00 2001 From: Vassiliy Date: Fri, 1 Aug 2014 22:49:57 +0400 Subject: [PATCH] IDEA-127205 Specify Editor Tab Width --- .../src/com/intellij/ide/ui/UISettings.java | 1 + .../editor/EditorTabsConfigurable.form | 35 ++++++++++--- .../editor/EditorTabsConfigurable.java | 26 ++++++++-- .../src/com/intellij/ui/tabs/TabInfo.java | 9 ++++ .../intellij/ui/tabs/impl/JBEditorTabs.java | 8 +++ .../com/intellij/ui/tabs/impl/TabLabel.java | 11 ++++ .../impl/EditorTabbedContainer.java | 2 + .../openapi/fileEditor/impl/EditorWindow.java | 8 ++- .../src/messages/ApplicationBundle.properties | 1 + .../openapi/util/text/StringUtil.java | 50 +++++++++++++++++++ .../intellij/util/text/StringUtilTest.java | 11 ++++ 11 files changed, 150 insertions(+), 12 deletions(-) diff --git a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java index cb0e1a298c9c..f97e7636bda2 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java +++ b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java @@ -72,6 +72,7 @@ public class UISettings implements PersistentStateComponent, Exporta public int RECENT_FILES_LIMIT = 50; public int CONSOLE_COMMAND_HISTORY_LIMIT = 300; public int EDITOR_TAB_LIMIT = 10; + public int EDITOR_TAB_TITLE_LIMIT = 30; public boolean ANIMATE_WINDOWS = true; public int ANIMATION_SPEED = 2000; // Pixels per second public boolean SHOW_TOOL_WINDOW_NUMBERS = true; diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form index 9872c7c624aa..db23ce17b2c3 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form @@ -3,7 +3,7 @@ - + @@ -108,7 +108,7 @@ - + @@ -119,7 +119,7 @@ - + @@ -129,7 +129,9 @@ - + + + @@ -138,19 +140,38 @@ - + + + + + + + + + + + + + + + + + + + + - + @@ -184,7 +205,7 @@ - + diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java index 4e1de817255a..8db2a882bfc7 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java @@ -45,6 +45,7 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { private JCheckBox myShowCloseButtonOnCheckBox; private JCheckBox myShowDirectoryInTabCheckBox; private JRadioButton myActivateRightNeighbouringTabRadioButton; + private JTextField myTabTitleLimitField; public EditorTabsConfigurable() { myEditorTabPlacement.setModel(new DefaultComboBoxModel(new Object[]{ @@ -119,6 +120,7 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { myHideKnownExtensions.setSelected(uiSettings.HIDE_KNOWN_EXTENSION_IN_TABS); myShowDirectoryInTabCheckBox.setSelected(uiSettings.SHOW_DIRECTORY_FOR_NON_UNIQUE_FILENAMES); myEditorTabLimitField.setText(Integer.toString(uiSettings.EDITOR_TAB_LIMIT)); + myTabTitleLimitField.setText(Integer.toString(uiSettings.EDITOR_TAB_TITLE_LIMIT)); myShowCloseButtonOnCheckBox.setSelected(uiSettings.SHOW_CLOSE_BUTTON); if (uiSettings.CLOSE_NON_MODIFIED_FILES_FIRST) { @@ -171,14 +173,29 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { uiSettings.ACTIVATE_RIGHT_EDITOR_ON_CLOSE = myActivateRightNeighbouringTabRadioButton.isSelected(); String temp = myEditorTabLimitField.getText(); - if(temp.trim().length() > 0){ + if (temp.trim().length() > 0) { try { int newEditorTabLimit = Integer.parseInt(temp); - if(newEditorTabLimit>0&&newEditorTabLimit!=uiSettings.EDITOR_TAB_LIMIT){ - uiSettings.EDITOR_TAB_LIMIT=newEditorTabLimit; + if (newEditorTabLimit > 0 && newEditorTabLimit != uiSettings.EDITOR_TAB_LIMIT) { + uiSettings.EDITOR_TAB_LIMIT = newEditorTabLimit; uiSettingsChanged = true; } - }catch (NumberFormatException ignored){} + } + catch (NumberFormatException ignored) { + } + } + temp = myTabTitleLimitField.getText(); + if (temp.trim().length() > 0) { + try { + int newTabTitleLimit = Integer.parseInt(temp); + newTabTitleLimit = Math.max(10, Math.min(100, newTabTitleLimit)); + if (newTabTitleLimit != uiSettings.EDITOR_TAB_TITLE_LIMIT){ + uiSettings.EDITOR_TAB_TITLE_LIMIT = newTabTitleLimit; + uiSettingsChanged = true; + } + } + catch (NumberFormatException ignored) { + } } if(uiSettingsChanged){ uiSettings.fireUISettingsChanged(); @@ -191,6 +208,7 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { boolean isModified = isModified(myCbModifiedTabsMarkedWithAsterisk, uiSettings.MARK_MODIFIED_TABS_WITH_ASTERISK); isModified |= isModified(myShowTabsTooltipsCheckBox, uiSettings.SHOW_TABS_TOOLTIPS); isModified |= isModified(myEditorTabLimitField, uiSettings.EDITOR_TAB_LIMIT); + isModified |= isModified(myTabTitleLimitField, uiSettings.EDITOR_TAB_TITLE_LIMIT); int tabPlacement = ((Integer)myEditorTabPlacement.getSelectedItem()).intValue(); isModified |= tabPlacement != uiSettings.EDITOR_TAB_PLACEMENT; isModified |= myHideKnownExtensions.isSelected() != uiSettings.HIDE_KNOWN_EXTENSION_IN_TABS; diff --git a/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java b/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java index d1dfa976e479..c8c944c86e4a 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java @@ -92,6 +92,7 @@ public final class TabInfo implements Queryable, PlaceProvider { * out of its container. (IDEA-61536) */ private WeakReference myPreviousSelection = new WeakReference(null); + private boolean myTitleShortened; public TabInfo(final JComponent component) { myComponent = component; @@ -392,6 +393,14 @@ public final class TabInfo implements Queryable, PlaceProvider { return myPreviousSelection.get(); } + public boolean isTitleShortened() { + return myTitleShortened; + } + + public void setTitleIsShortened(boolean titleIsShortened) { + myTitleShortened = titleIsShortened; + } + public interface DragOutDelegate { void dragOutStarted(MouseEvent mouseEvent, TabInfo info); diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBEditorTabs.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBEditorTabs.java index 8e5844fd4684..b08f6c9489a5 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBEditorTabs.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBEditorTabs.java @@ -44,6 +44,7 @@ import java.util.List; */ public class JBEditorTabs extends JBTabsImpl { public static final String TABS_ALPHABETICAL_KEY = "tabs.alphabetical"; + static final String TABS_CHORTEN_TITLE_IF_NEED = "tabs.shorten.title.if.need"; private JBEditorTabsPainter myDarkPainter = new DarculaEditorTabsPainter(); private JBEditorTabsPainter myDefaultPainter = new DefaultEditorTabsPainter(); @@ -60,6 +61,13 @@ public class JBEditorTabs extends JBTabsImpl { return super.createSingleRowLayout(); } + @Override + protected TabLabel createTabLabel(TabInfo info) { + TabLabel label = super.createTabLabel(info); + label.putClientProperty(TABS_CHORTEN_TITLE_IF_NEED, Boolean.TRUE); + return label; + } + @Override public boolean isEditorTabs() { return true; diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java index 21c36d5dd66a..b85fbcad0f47 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java @@ -22,6 +22,7 @@ import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.util.Pass; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.*; import com.intellij.ui.components.panels.Wrapper; import com.intellij.ui.tabs.JBTabsPosition; @@ -317,6 +318,16 @@ public class TabLabel extends JPanel { public void setText(final SimpleColoredText text) { + myInfo.setTitleIsShortened(false); + if (text != null && text.getTexts().size() == 1 && Boolean.TRUE == getClientProperty(JBEditorTabs.TABS_CHORTEN_TITLE_IF_NEED)) { + String title = text.getTexts().get(0); + if (title.length() > UISettings.getInstance().EDITOR_TAB_TITLE_LIMIT) { + SimpleTextAttributes attributes = text.getAttributes().get(0); + text.clear(); + text.append(StringUtil.getShortened(title, UISettings.getInstance().EDITOR_TAB_TITLE_LIMIT), attributes); + myInfo.setTitleIsShortened(true); + } + } myLabel.change(new Runnable() { public void run() { myLabel.clear(); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index c2353d5db207..03b03faffe03 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -277,6 +277,8 @@ public final class EditorTabbedContainer implements Disposable, CloseAction.Clos myTabs.getTabAt(index).setTooltipText(text); } + public boolean isTitleShortened(int index) { return myTabs.getTabAt(index).isTitleShortened(); } + public void setBackgroundColorAt(final int index, final Color color) { myTabs.getTabAt(index).setTabColor(color); } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java index 14b2afe16345..06a3966ade1d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java @@ -464,6 +464,10 @@ public class EditorWindow { } } + private boolean isTitleShortenedAt(int index) { + return myTabbedPane != null && myTabbedPane.isTitleShortened(index); + } + private void setBackgroundColorAt(final int index, final Color color) { if (myTabbedPane != null) { myTabbedPane.setBackgroundColorAt(index, color); @@ -908,7 +912,9 @@ public class EditorWindow { final int index = findEditorIndex(findFileComposite(file)); if (index != -1) { setTitleAt(index, EditorTabbedContainer.calcTabTitle(getManager().getProject(), file)); - setToolTipTextAt(index, UISettings.getInstance().SHOW_TABS_TOOLTIPS ? getManager().getFileTooltipText(file) : null); + setToolTipTextAt(index, UISettings.getInstance().SHOW_TABS_TOOLTIPS || isTitleShortenedAt(index) + ? getManager().getFileTooltipText(file) + : null); } } diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index bbd737afedf1..5d539357ca3d 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -360,6 +360,7 @@ label.collapse.by.default=Collapse by default: checkbox.show.code.folding.outline=Show code folding outline group.tab.appearance=Tab Appearance editbox.tab.limit=Tab limit: +editbox.tab.title.limit=Tab title limit: combobox.editor.tab.placement=Placement: checkbox.editor.tabs.in.single.row=Show tabs in single row checkbox.editor.tabs.show.close.button=Show "close" button on editor tabs diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index 3556f1cce3bd..61ec6edb41cd 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -2658,6 +2658,56 @@ public class StringUtil extends StringUtilRt { return s.startsWith(smallPart.toLowerCase()) && bigPart.toLowerCase().startsWith(s); } + public static String getShortened(String s, int maxWidth) { + int length = s.length(); + if (isEmpty(s) || length <= maxWidth) return s; + ArrayList words = new ArrayList(); + + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < length; i++) { + char ch = s.charAt(i); + + if (i == length - 1) { + builder.append(ch); + words.add(builder.toString()); + builder.delete(0, builder.length()); + continue; + } + + if (i > 0 && (ch == '/' || ch == '.' || Character.isUpperCase(ch))) { + words.add(builder.toString()); + builder.delete(0, builder.length()); + } + builder.append(ch); + } + + int removedLength = 0; + + String toPaste = "..."; + int index; + while (true) { + index = Math.max(0, words.size() / 2 - 1); + String aWord = words.get(index); + words.remove(index); + if (words.size() < 2) { + int toCut = length - removedLength - maxWidth + 3; + int pos = (aWord.length() - toCut) / 2; + toPaste = aWord.substring(0, pos) + "..." + aWord.substring(pos+toCut); + break; + } + removedLength += aWord.length(); + if (length - removedLength <= maxWidth - 3) { + break; + } + } + for (int i = 0; i < words.size(); i++) { + String word = words.get(i); + if (i == index || words.size() == 1) builder.append(toPaste); + builder.append(word); + } + return builder.toString().replaceAll("\\.{4,}", "..."); + } + /** * Expirable CharSequence. Very useful to control external library execution time, * i.e. when java.util.regex.Pattern match goes out of control. diff --git a/platform/util/testSrc/com/intellij/util/text/StringUtilTest.java b/platform/util/testSrc/com/intellij/util/text/StringUtilTest.java index 2f31dc4f4e61..e1d28d247d72 100644 --- a/platform/util/testSrc/com/intellij/util/text/StringUtilTest.java +++ b/platform/util/testSrc/com/intellij/util/text/StringUtilTest.java @@ -195,4 +195,15 @@ public class StringUtilTest extends TestCase { assertEquals(Arrays.asList("\n", "\r\n", "\n", "\r\n", "\r", "\r", "aa\r", "bb\r\n", "cc\n", "\r", "dd\n", "\n", "\r\n", "\r"), Arrays.asList(StringUtil.splitByLinesKeepSeparators("\n\r\n\n\r\n\r\raa\rbb\r\ncc\n\rdd\n\n\r\n\r"))); } + + public void testShortened() { + String[] names = {"AVeryVeeryLongClassName.java", "com.test.SomeJAVAClassName.java", "strangelowercaseclassname.java"}; + for (String name : names) { + for (int i = name.length() + 1; i > 15; i--) { + String shortened = StringUtil.getShortened(name, i); + assertTrue(shortened.length() <= i); + assertTrue(!shortened.contains("....")); + } + } + } }