diff --git a/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java b/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java index 844ef3f21630..b8177a9fe432 100644 --- a/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java +++ b/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java @@ -19,6 +19,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.SystemInfo; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.Topic; import org.jetbrains.annotations.NotNull; @@ -95,4 +96,24 @@ public interface StatusBar extends StatusBarInfo, Disposable { IdeFrame getFrame(); void install(IdeFrame frame); + + class Anchors { + public static final String DEFAULT_ANCHOR = after(SystemInfo.isMac + ? StandardWidgets.ENCODING_PANEL + : StandardWidgets.INSERT_OVERWRITE_PANEL); + + public static String before(String widgetId) { + return "before " + widgetId; + } + public static String after(String widgetId) { + return "after " + widgetId; + } + } + + class StandardWidgets { + public static final String ENCODING_PANEL = "Encoding"; + public static final String INSERT_OVERWRITE_PANEL = "InsertOverwrite"; + public static final String READONLY_ATTRIBUTE_PANEL = "ReadOnlyAttribute"; + public static final String POSITION_PANEL = "Position"; + } } diff --git a/platform/platform-api/src/com/intellij/openapi/wm/StatusBarWidgetProvider.java b/platform/platform-api/src/com/intellij/openapi/wm/StatusBarWidgetProvider.java new file mode 100644 index 000000000000..62c8d85fb7c9 --- /dev/null +++ b/platform/platform-api/src/com/intellij/openapi/wm/StatusBarWidgetProvider.java @@ -0,0 +1,36 @@ +// Copyright 2000-2018 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.openapi.wm; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Extension point to assist with adding new status bar widgets + */ +public interface StatusBarWidgetProvider { + ExtensionPointName EP_NAME = new ExtensionPointName<>("com.intellij.statusBarWidgetProvider"); + + /** + * Returns a widget to be added to the status bar. + * Returning null means that no widget should be added. + * + * Normally you should return a new instance of your widget here. + * @param project Current project + * @return Widget or null + */ + @Nullable + StatusBarWidget getWidget(@NotNull Project project); + + /** + * Determines position of the added widget in relation to other widgets on the status bar. + * + * Utility methods from StatusBar.Anchors can be used to create an anchor with 'before' or 'after' rules. + * Take a look at StatusBar.StandardWidgets if you need to position your widget relatively to one of the standard widgets. + */ + @NotNull + default String getAnchor() { + return StatusBar.Anchors.DEFAULT_ANCHOR; + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java index fc7c8469e4fa..c8b95a2428a5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java @@ -41,6 +41,7 @@ import com.intellij.openapi.wm.impl.status.*; import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrame; import com.intellij.ui.*; import com.intellij.ui.mac.MacMainFrameDecorator; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.accessibility.AccessibleContextAccessor; @@ -56,6 +57,7 @@ import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.io.File; import java.lang.reflect.Field; +import java.util.Set; /** * @author Anton Katilin @@ -438,34 +440,36 @@ public class IdeFrameImpl extends JFrame implements IdeFrameEx, AccessibleContex } } + private final Set widgetIDs = ContainerUtil.newHashSet(); + private void addWidget(StatusBar statusBar, StatusBarWidget widget, String anchor) { + if (!widgetIDs.add(widget.ID())) { + LOG.error("Attempting to add more than one widget with ID: " + widget.ID()); + return; + } + statusBar.addWidget(widget, anchor); + } + private void installDefaultProjectStatusBarWidgets(@NotNull final Project project) { final StatusBar statusBar = getStatusBar(); + addWidget(statusBar, new PositionPanel(project), StatusBar.Anchors.before(IdeMessagePanel.FATAL_ERROR)); + addWidget(statusBar, new IdeNotificationArea(), StatusBar.Anchors.before(IdeMessagePanel.FATAL_ERROR)); + addWidget(statusBar, new EncodingPanel(project), StatusBar.Anchors.after(StatusBar.StandardWidgets.POSITION_PANEL)); + addWidget(statusBar, new LineSeparatorPanel(project), StatusBar.Anchors.before(StatusBar.StandardWidgets.ENCODING_PANEL)); + addWidget(statusBar, new InsertOverwritePanel(project), StatusBar.Anchors.after(StatusBar.StandardWidgets.ENCODING_PANEL)); + addWidget(statusBar, new ToggleReadOnlyAttributePanel(project), + StatusBar.Anchors.after(StatusBar.StandardWidgets.INSERT_OVERWRITE_PANEL)); - final PositionPanel positionPanel = new PositionPanel(project); - statusBar.addWidget(positionPanel, "before " + IdeMessagePanel.FATAL_ERROR); - - final IdeNotificationArea notificationArea = new IdeNotificationArea(); - statusBar.addWidget(notificationArea, "before " + IdeMessagePanel.FATAL_ERROR); - - final EncodingPanel encodingPanel = new EncodingPanel(project); - statusBar.addWidget(encodingPanel, "after Position"); - - final LineSeparatorPanel lineSeparatorPanel = new LineSeparatorPanel(project); - statusBar.addWidget(lineSeparatorPanel, "before " + encodingPanel.ID()); - - final ToggleReadOnlyAttributePanel readOnlyAttributePanel = new ToggleReadOnlyAttributePanel(project); - - final InsertOverwritePanel insertOverwritePanel = new InsertOverwritePanel(project); - statusBar.addWidget(insertOverwritePanel, "after Encoding"); - statusBar.addWidget(readOnlyAttributePanel, "after InsertOverwrite"); + for (StatusBarWidgetProvider widgetProvider: StatusBarWidgetProvider.EP_NAME.getExtensions()) { + StatusBarWidget widget = widgetProvider.getWidget(project); + if (widget == null) continue; + addWidget(statusBar, widget, widgetProvider.getAnchor()); + } Disposer.register(project, () -> { - statusBar.removeWidget(encodingPanel.ID()); - statusBar.removeWidget(lineSeparatorPanel.ID()); - statusBar.removeWidget(positionPanel.ID()); - statusBar.removeWidget(notificationArea.ID()); - statusBar.removeWidget(readOnlyAttributePanel.ID()); - statusBar.removeWidget(insertOverwritePanel.ID()); + for (String widgetID: widgetIDs) { + statusBar.removeWidget(widgetID); + } + widgetIDs.clear(); //noinspection deprecation ((StatusBarEx)statusBar).removeCustomIndicationComponents(); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeRootPane.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeRootPane.java index 133b40a1f1e7..b91ac3728321 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeRootPane.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeRootPane.java @@ -263,12 +263,12 @@ public class IdeRootPane extends JRootPane implements UISettingsListener, Dispos public void dispose() { componentFactory.disposeComponent(myStatusBar, c); } - }, "before " + MemoryUsagePanel.WIDGET_ID); + }, StatusBar.Anchors.before(MemoryUsagePanel.WIDGET_ID)); } } myStatusBar.addWidget(myMemoryWidget); - myStatusBar.addWidget(new IdeMessagePanel(frame, MessagePool.getInstance()), "before " + MemoryUsagePanel.WIDGET_ID); + myStatusBar.addWidget(new IdeMessagePanel(frame, MessagePool.getInstance()), StatusBar.Anchors.before(MemoryUsagePanel.WIDGET_ID)); setMemoryIndicatorVisible(UISettings.getInstance().getShowMemoryIndicator()); } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EditorBasedStatusBarPopup.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EditorBasedStatusBarPopup.java index 50663e75c6b6..368a6b666d14 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EditorBasedStatusBarPopup.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EditorBasedStatusBarPopup.java @@ -204,6 +204,12 @@ public abstract class EditorBasedStatusBarPopup extends EditorBasedWidget implem } protected static class WidgetState { + + public static final WidgetState HIDDEN = new WidgetState("", "", false); + static { + HIDDEN.setHidden(true); + } + protected WidgetState(String toolTip, String text, boolean actionEnabled) { this.toolTip = toolTip; this.text = text; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EncodingPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EncodingPanel.java index a1ac2ad25f57..19ec9059e177 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EncodingPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EncodingPanel.java @@ -17,6 +17,7 @@ import com.intellij.openapi.vfs.encoding.EncodingManager; import com.intellij.openapi.vfs.encoding.EncodingManagerImpl; import com.intellij.openapi.vfs.encoding.EncodingUtil; import com.intellij.openapi.vfs.impl.BulkVirtualFileListenerAdapter; +import com.intellij.openapi.wm.StatusBar; import com.intellij.openapi.wm.StatusBarWidget; import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; @@ -32,8 +33,9 @@ public class EncodingPanel extends EditorBasedStatusBarPopup { @NotNull @Override protected WidgetState getWidgetState(@Nullable VirtualFile file) { - // this is done to preserve the old behavior; not sure if it is correct - if (file == null) return new WidgetState("", "", false); + if (file == null) { + return WidgetState.HIDDEN; + } Pair check = EncodingUtil.getCharsetAndTheReasonTooltip(file); String failReason = check == null ? null : check.second; @@ -78,6 +80,6 @@ public class EncodingPanel extends EditorBasedStatusBarPopup { @Override @NotNull public String ID() { - return "Encoding"; + return StatusBar.StandardWidgets.ENCODING_PANEL; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InsertOverwritePanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InsertOverwritePanel.java index dc7bec6e76df..27b166fd1718 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InsertOverwritePanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InsertOverwritePanel.java @@ -50,7 +50,7 @@ public class InsertOverwritePanel extends EditorBasedWidget implements StatusBar @Override @NotNull public String ID() { - return "InsertOverwrite"; + return StatusBar.StandardWidgets.INSERT_OVERWRITE_PANEL; } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/LineSeparatorPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/LineSeparatorPanel.java index 489749716901..7014a3dca17c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/LineSeparatorPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/LineSeparatorPanel.java @@ -24,7 +24,10 @@ public class LineSeparatorPanel extends EditorBasedStatusBarPopup { @NotNull @Override protected WidgetState getWidgetState(@Nullable VirtualFile file) { - String lineSeparator = file == null ? null : LoadTextUtil.detectLineSeparator(file, true); + if (file == null) { + return WidgetState.HIDDEN; + } + String lineSeparator = LoadTextUtil.detectLineSeparator(file, true); String toolTipText; String panelText; if (lineSeparator != null) { @@ -33,7 +36,7 @@ public class LineSeparatorPanel extends EditorBasedStatusBarPopup { } else { toolTipText = "No line separator"; - panelText = file != null ? "n/a" : ""; + panelText = "n/a"; } return new WidgetState(toolTipText, panelText, lineSeparator != null); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PositionPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PositionPanel.java index a12de55461ef..0db3393bc402 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PositionPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PositionPanel.java @@ -48,7 +48,6 @@ public class PositionPanel extends EditorBasedWidget public static final String SPACE = " "; public static final String SEPARATOR = ":"; public static final String MAX_POSSIBLE_TEXT = "0000000000000"; - public static final String ID = "Position"; private static final int CHAR_COUNT_SYNC_LIMIT = 500_000; private static final String CHAR_COUNT_UNKNOWN = "..."; @@ -71,7 +70,7 @@ public class PositionPanel extends EditorBasedWidget @Override @NotNull public String ID() { - return ID; + return StatusBar.StandardWidgets.POSITION_PANEL; } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToggleReadOnlyAttributePanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToggleReadOnlyAttributePanel.java index 2048002a4699..e078aaec74f9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToggleReadOnlyAttributePanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToggleReadOnlyAttributePanel.java @@ -60,7 +60,7 @@ public class ToggleReadOnlyAttributePanel implements StatusBarWidget.Multiframe, @Override @NotNull public String ID() { - return "ReadOnlyAttribute"; + return StatusBar.StandardWidgets.READONLY_ATTRIBUTE_PANEL; } @Override diff --git a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml index 139be9e746b3..1cec2eab4114 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml @@ -181,6 +181,7 @@ +