dd3f8e96ce426f6e62afefbd7908937a982b7ad0
[idea/community.git] / platform / lang-impl / src / com / intellij / ide / navigationToolbar / NavBarRootPaneExtension.java
1 // 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.
2 package com.intellij.ide.navigationToolbar;
3
4 import com.intellij.ide.navigationToolbar.ui.NavBarUIManager;
5 import com.intellij.ide.ui.UISettings;
6 import com.intellij.ide.ui.UISettingsListener;
7 import com.intellij.ide.ui.customization.CustomActionsSchema;
8 import com.intellij.ide.ui.customization.CustomisedActionGroup;
9 import com.intellij.openapi.actionSystem.*;
10 import com.intellij.openapi.actionSystem.ex.ComboBoxAction;
11 import com.intellij.openapi.project.Project;
12 import com.intellij.openapi.wm.IdeRootPaneNorthExtension;
13 import com.intellij.openapi.wm.impl.IdeFrameImpl;
14 import com.intellij.ui.ScrollPaneFactory;
15 import com.intellij.util.ui.JBSwingUtilities;
16 import com.intellij.util.ui.JBUI;
17 import kotlin.Unit;
18 import org.jetbrains.annotations.NonNls;
19 import org.jetbrains.annotations.NotNull;
20 import org.jetbrains.annotations.Nullable;
21
22 import javax.swing.*;
23 import java.awt.*;
24
25 /**
26  * @author Konstantin Bulenkov
27  */
28 public final class NavBarRootPaneExtension extends IdeRootPaneNorthExtension {
29   private JComponent myWrapperPanel;
30   @NonNls public static final String NAV_BAR = "NavBar";
31   @SuppressWarnings("StatefulEp")
32   private Project myProject;
33   private NavBarPanel myNavigationBar;
34   private JPanel myRunPanel;
35   private final boolean myNavToolbarGroupExist;
36   private JScrollPane myScrollPane;
37
38   public NavBarRootPaneExtension(@NotNull Project project) {
39     myProject = project;
40
41     myProject.getMessageBus().connect().subscribe(UISettingsListener.TOPIC, uiSettings -> {
42       toggleRunPanel(!uiSettings.getShowMainToolbar() && uiSettings.getShowNavigationBar() && !uiSettings.getPresentationMode());
43     });
44
45     myNavToolbarGroupExist = runToolbarExists();
46   }
47
48   @Override
49   public void revalidate() {
50     final UISettings settings = UISettings.getInstance();
51     if (!settings.getShowMainToolbar() && settings.getShowNavigationBar() && !UISettings.getInstance().getPresentationMode()) {
52       toggleRunPanel(false);
53       toggleRunPanel(true);
54     }
55   }
56
57   @Override
58   public IdeRootPaneNorthExtension copy() {
59     return new NavBarRootPaneExtension(myProject);
60   }
61
62   public boolean isMainToolbarVisible() {
63     return !UISettings.getInstance().getPresentationMode() && (UISettings.getInstance().getShowMainToolbar() || !myNavToolbarGroupExist);
64   }
65
66   public static boolean runToolbarExists() {
67     final AnAction correctedAction = CustomActionsSchema.getInstance().getCorrectedAction("NavBarToolBar");
68     return correctedAction instanceof DefaultActionGroup && ((DefaultActionGroup)correctedAction).getChildrenCount() > 0 ||
69            correctedAction instanceof CustomisedActionGroup && ((CustomisedActionGroup)correctedAction).getFirstAction() != null;
70   }
71
72   @NotNull
73   @Override
74   public JComponent getComponent() {
75     if (myWrapperPanel == null) {
76       myWrapperPanel = new NavBarWrapperPanel(new BorderLayout()) {
77         @Override
78         protected void paintComponent(Graphics g) {
79           super.paintComponent(g);
80           NavBarUIManager.getUI().doPaintWrapperPanel((Graphics2D)g, getBounds(), isMainToolbarVisible());
81         }
82
83         @Override
84         public Insets getInsets() {
85           return NavBarUIManager.getUI().getWrapperPanelInsets(super.getInsets());
86         }
87       };
88
89       addNavigationBarPanel(myWrapperPanel);
90
91       toggleRunPanel(!UISettings.getInstance().getShowMainToolbar() && !UISettings.getInstance().getPresentationMode());
92     }
93     return myWrapperPanel;
94   }
95
96   private void addNavigationBarPanel(JComponent wrapperPanel) {
97     wrapperPanel.add(buildNavBarPanel(), BorderLayout.CENTER);
98   }
99
100   public static class NavBarWrapperPanel extends JPanel {
101     public NavBarWrapperPanel(LayoutManager layout) {
102       super(layout);
103       setName("navbar");
104     }
105
106     @Override
107     protected Graphics getComponentGraphics(Graphics graphics) {
108       return JBSwingUtilities.runGlobalCGTransform(this, super.getComponentGraphics(graphics));
109     }
110   }
111
112   private static void alignVertically(Container container) {
113     if (container.getComponentCount() == 1) {
114       Component c = container.getComponent(0);
115       Insets insets = container.getInsets();
116       Dimension d = c.getPreferredSize();
117       Rectangle r = container.getBounds();
118       c.setBounds(insets.left, (r.height - d.height - insets.top - insets.bottom) / 2 + insets.top, r.width - insets.left - insets.right, d.height);
119     }
120   }
121
122   private void toggleRunPanel(final boolean show) {
123     if (show && myRunPanel == null && runToolbarExists()) {
124       final ActionManager manager = ActionManager.getInstance();
125       AnAction toolbarRunGroup = CustomActionsSchema.getInstance().getCorrectedAction("NavBarToolBar");
126
127       if (toolbarRunGroup instanceof ActionGroup && myWrapperPanel != null) {
128         final ActionToolbar actionToolbar = manager.createActionToolbar(ActionPlaces.NAVIGATION_BAR_TOOLBAR, (ActionGroup)toolbarRunGroup, true);
129         final JComponent component = actionToolbar.getComponent();
130         myRunPanel = new JPanel(new BorderLayout()) {
131           @Override
132           public void doLayout() {
133             alignVertically(this);
134           }
135         };
136         myRunPanel.setOpaque(false);
137         myRunPanel.add(component, BorderLayout.CENTER);
138         final boolean needGap = isNeedGap(toolbarRunGroup);
139         myRunPanel.setBorder(JBUI.Borders.emptyLeft(needGap ? 5 : 1));
140         myWrapperPanel.add(myRunPanel, BorderLayout.EAST);
141       }
142     }
143     else if (!show && myRunPanel != null) {
144       myWrapperPanel.remove(myRunPanel);
145       myRunPanel = null;
146     }
147   }
148
149   private boolean isUndocked() {
150     final Window ancestor = SwingUtilities.getWindowAncestor(myWrapperPanel);
151     return (ancestor != null && !(ancestor instanceof IdeFrameImpl))
152            || !UISettings.getInstance().getShowMainToolbar()
153            || !UISettings.getInstance().getPresentationMode();
154   }
155
156   private static boolean isNeedGap(final AnAction group) {
157     final AnAction firstAction = getFirstAction(group);
158     return firstAction instanceof ComboBoxAction;
159   }
160
161   @Nullable
162   private static AnAction getFirstAction(final AnAction group) {
163     if (group instanceof DefaultActionGroup) {
164       AnAction firstAction = null;
165       for (final AnAction action : ((DefaultActionGroup)group).getChildActionsOrStubs()) {
166         if (action instanceof DefaultActionGroup) {
167           firstAction = getFirstAction(action);
168         }
169         else if (action instanceof Separator || action instanceof ActionGroup) {
170           continue;
171         }
172         else {
173           firstAction = action;
174           break;
175         }
176
177         if (firstAction != null) break;
178       }
179
180       return firstAction;
181     }
182     if (group instanceof CustomisedActionGroup) {
183       return ((CustomisedActionGroup)group).getFirstAction();
184     }
185     return null;
186   }
187
188   private JComponent buildNavBarPanel() {
189     myNavigationBar = new NavBarPanel(myProject, true);
190     myWrapperPanel.putClientProperty("NavBarPanel", myNavigationBar);
191     myNavigationBar.getModel().setFixedComponent(true);
192     myScrollPane = ScrollPaneFactory.createScrollPane(myNavigationBar);
193
194     JPanel panel = new JPanel(new BorderLayout()) {
195
196       @Override
197       protected void paintComponent(Graphics g) {
198         super.paintComponent(g);
199         final Component navBar = myScrollPane;
200         Insets insets = getInsets();
201         Rectangle r = navBar.getBounds();
202
203         Graphics2D g2d = (Graphics2D) g.create();
204         g2d.translate(r.x, r.y);
205         g2d.dispose();
206       }
207
208       @Override
209       public void doLayout() {
210         // align vertically
211         final Rectangle r = getBounds();
212         final Insets insets = getInsets();
213         int x = insets.left;
214         if (myScrollPane == null) return;
215         final Component navBar = myScrollPane;
216
217         final Dimension preferredSize = navBar.getPreferredSize();
218
219         navBar.setBounds(x, (r.height - preferredSize.height) / 2,
220                          r.width - insets.left - insets.right, preferredSize.height);
221       }
222
223       @Override
224       public void updateUI() {
225         super.updateUI();
226         setOpaque(true);
227         if (myScrollPane == null || myNavigationBar == null) return;
228
229         myScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
230         myScrollPane.setHorizontalScrollBar(null);
231         myScrollPane.setBorder(new NavBarBorder());
232         myScrollPane.setOpaque(false);
233         myScrollPane.getViewport().setOpaque(false);
234         myScrollPane.setViewportBorder(null);
235         myNavigationBar.setBorder(null);
236       }
237     };
238
239     panel.add(myScrollPane, BorderLayout.CENTER);
240     panel.updateUI();
241     return panel;
242   }
243
244   @Override
245   public void uiSettingsChanged(@NotNull UISettings settings) {
246     if (myNavigationBar == null) {
247       return;
248     }
249
250     myNavigationBar.updateState(settings.getShowNavigationBar());
251     myWrapperPanel.setVisible(settings.getShowNavigationBar() && !UISettings.getInstance().getPresentationMode());
252
253     myWrapperPanel.revalidate();
254     myNavigationBar.revalidate();
255     myWrapperPanel.repaint();
256
257     if (myWrapperPanel.getComponentCount() > 0) {
258       Component c = myWrapperPanel.getComponent(0);
259       if (c instanceof JComponent) {
260         ((JComponent)c).setOpaque(false);
261       }
262     }
263   }
264
265   @Override
266   @NotNull
267   public String getKey() {
268     return NAV_BAR;
269   }
270 }