1 /* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package ohos.devtools.views.trace; 17 18 import com.intellij.ui.JBColor; 19 import com.intellij.ui.UtilUiBundle; 20 import com.intellij.util.containers.ContainerUtil; 21 import com.intellij.util.ui.StartupUiUtil; 22 import com.intellij.util.ui.UIUtil; 23 import org.jetbrains.annotations.Nls; 24 import org.jetbrains.annotations.NonNls; 25 import org.jetbrains.annotations.NotNull; 26 27 import javax.swing.AbstractAction; 28 import javax.swing.ActionMap; 29 import javax.swing.Icon; 30 import javax.swing.InputMap; 31 import javax.swing.JButton; 32 import javax.swing.JComponent; 33 import javax.swing.JPanel; 34 import javax.swing.KeyStroke; 35 import java.awt.Color; 36 import java.awt.Dimension; 37 import java.awt.Font; 38 import java.awt.Graphics; 39 import java.awt.GridBagConstraints; 40 import java.awt.GridBagLayout; 41 import java.awt.Insets; 42 import java.awt.Label; 43 import java.awt.event.ActionEvent; 44 import java.awt.event.ActionListener; 45 import java.awt.event.KeyEvent; 46 import java.util.Collection; 47 48 /** 49 * FolderPanel 50 * 51 * @since 2021/5/18 23:18 52 */ 53 public class FolderPanel extends JPanel { 54 /** 55 * LEFT_KEY_STROKE 56 */ 57 public static final KeyStroke LEFT_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0); 58 59 /** 60 * LEFT_KEY_STROKE 61 */ 62 public static final KeyStroke RIGHT_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0); 63 64 /** 65 * expand word 66 */ 67 @NonNls 68 public static final String EXPAND = "expand"; 69 70 /** 71 * collapse word 72 */ 73 @NonNls 74 public static final String COLLAPSE = "collapse"; 75 76 private final JButton myToggleCollapseButton; 77 private final JComponent myContent; 78 private final Collection<CollapsingListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); 79 private final Icon myExpandIcon; 80 private final Icon myCollapseIcon; 81 private boolean myIsCollapsed; 82 private boolean myIsInitialized = false; 83 private Label myTitleLabel; 84 85 /** 86 * structure 87 * 88 * @param content content 89 * @param collapseButtonAtLeft collapseButtonAtLeft 90 * @param isCollapsed isCollapsed 91 * @param collapseIcon collapseIcon 92 * @param expandIcon expandIcon 93 * @param title title 94 */ FolderPanel(JComponent content, boolean collapseButtonAtLeft, boolean isCollapsed, Icon collapseIcon, Icon expandIcon, String title)95 public FolderPanel(JComponent content, boolean collapseButtonAtLeft, boolean isCollapsed, Icon collapseIcon, 96 Icon expandIcon, String title) { 97 super(new GridBagLayout()); 98 myContent = content; 99 setBackground(content.getBackground()); 100 myExpandIcon = expandIcon; 101 myCollapseIcon = collapseIcon; 102 myToggleCollapseButton = new JButton(); 103 myToggleCollapseButton.setOpaque(false); 104 myToggleCollapseButton.setBorderPainted(false); 105 myToggleCollapseButton.setBackground(new Color(0, 0, 0, 0)); 106 final Dimension buttonDimension = getButtonDimension(); 107 myToggleCollapseButton.setSize(buttonDimension); 108 myToggleCollapseButton.setPreferredSize(buttonDimension); 109 myToggleCollapseButton.setMinimumSize(buttonDimension); 110 myToggleCollapseButton.setMaximumSize(buttonDimension); 111 myToggleCollapseButton.setFocusable(true); 112 myToggleCollapseButton.getActionMap().put(COLLAPSE, new AbstractAction() { 113 @Override 114 public void actionPerformed(ActionEvent event) { 115 collapse(); 116 } 117 }); 118 myToggleCollapseButton.getActionMap().put(EXPAND, new AbstractAction() { 119 @Override 120 public void actionPerformed(ActionEvent event) { 121 expand(); 122 } 123 }); 124 myToggleCollapseButton.getInputMap().put(LEFT_KEY_STROKE, COLLAPSE); 125 myToggleCollapseButton.getInputMap().put(RIGHT_KEY_STROKE, EXPAND); 126 final int iconAnchor = collapseButtonAtLeft ? GridBagConstraints.WEST : GridBagConstraints.EAST; 127 add(myToggleCollapseButton, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, iconAnchor, GridBagConstraints.CENTER, 128 new Insets(0, collapseButtonAtLeft ? 0 : 5, 0, collapseButtonAtLeft ? 0 : 0), 0, 0)); 129 if (title != null) { 130 myTitleLabel = new Label(title); 131 myTitleLabel.setFont(StartupUiUtil.getLabelFont().deriveFont(Font.BOLD)); 132 myTitleLabel.setBackground(content.getBackground()); 133 add(myTitleLabel, 134 new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, 135 new Insets(0, 20, 0, 0), 0, 0)); 136 } 137 myToggleCollapseButton.addActionListener(new ActionListener() { 138 @Override 139 public void actionPerformed(ActionEvent event) { 140 setCollapsed(!myIsCollapsed); 141 } 142 }); 143 setCollapsed(isCollapsed); 144 } 145 146 /** 147 * structure 148 * 149 * @param content content 150 * @param collapseButtonAtLeft collapseButtonAtLeft 151 */ FolderPanel(JComponent content, boolean collapseButtonAtLeft)152 public FolderPanel(JComponent content, boolean collapseButtonAtLeft) { 153 this(content, collapseButtonAtLeft, false, null, null, null); 154 } 155 getButtonDimension()156 private Dimension getButtonDimension() { 157 if (myExpandIcon == null) { 158 return new Dimension(7, 7); 159 } else { 160 return new Dimension(myExpandIcon.getIconWidth(), myExpandIcon.getIconHeight()); 161 } 162 } 163 164 @Nls getToggleButtonToolTipText()165 private String getToggleButtonToolTipText() { 166 if (myIsCollapsed) { 167 return UtilUiBundle.message("collapsible.panel.collapsed.state.tooltip.text"); 168 } else { 169 return UtilUiBundle.message("collapsible.panel.expanded.state.tooltip.text"); 170 } 171 } 172 getIcon()173 private Icon getIcon() { 174 if (myIsCollapsed) { 175 return myExpandIcon; 176 } else { 177 return myCollapseIcon; 178 } 179 } 180 notifyListeners()181 private void notifyListeners() { 182 for (CollapsingListener listener : myListeners) { 183 listener.onCollapsingChanged(this, isCollapsed()); 184 } 185 } 186 187 /** 188 * addCollapsingListener 189 * 190 * @param listener listener 191 */ addCollapsingListener(CollapsingListener listener)192 public void addCollapsingListener(CollapsingListener listener) { 193 myListeners.add(listener); 194 } 195 196 /** 197 * removeCollapsingListener 198 * 199 * @param listener listener 200 */ removeCollapsingListener(CollapsingListener listener)201 public void removeCollapsingListener(CollapsingListener listener) { 202 myListeners.remove(listener); 203 } 204 205 /** 206 * get myIsCollapsed 207 * 208 * @return isCollapsed 209 */ isCollapsed()210 public boolean isCollapsed() { 211 return myIsCollapsed; 212 } 213 214 /** 215 * set the collapse 216 * 217 * @param collapse collapse 218 */ setCollapsed(boolean collapse)219 protected void setCollapsed(boolean collapse) { 220 try { 221 if (collapse) { 222 if (myIsInitialized) { 223 remove(myContent); 224 } 225 } else { 226 add(myContent, 227 new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, 228 new Insets(0, 0, 0, 0), 0, 0)); 229 } 230 myIsCollapsed = collapse; 231 Icon icon = getIcon(); 232 if (icon != null) { 233 myToggleCollapseButton.setIcon(icon); 234 myToggleCollapseButton.setBorder(null); 235 myToggleCollapseButton.setBorderPainted(false); 236 myToggleCollapseButton.setToolTipText(getToggleButtonToolTipText()); 237 } 238 if (collapse) { 239 setFocused(true); 240 setSelected(true); 241 } else { 242 myContent.requestFocusInWindow(); 243 } 244 notifyListeners(); 245 revalidate(); 246 repaint(); 247 } finally { 248 myIsInitialized = true; 249 } 250 } 251 252 /** 253 * expand 254 */ expand()255 public void expand() { 256 if (myIsCollapsed) { 257 setCollapsed(false); 258 } 259 } 260 261 /** 262 * collapse 263 */ collapse()264 public void collapse() { 265 if (!myIsCollapsed) { 266 setCollapsed(true); 267 } 268 } 269 270 /** 271 * setFocused 272 * 273 * @param focused focused 274 */ setFocused(boolean focused)275 public void setFocused(boolean focused) { 276 myToggleCollapseButton.requestFocusInWindow(); 277 } 278 279 /** 280 * setSelected 281 * 282 * @param selected selected 283 */ setSelected(boolean selected)284 public void setSelected(boolean selected) { 285 myToggleCollapseButton.setSelected(selected); 286 } 287 288 /** 289 * getCollapsibleActionMap 290 * 291 * @return InputMap 292 */ getCollapsibleActionMap()293 public ActionMap getCollapsibleActionMap() { 294 return myToggleCollapseButton.getActionMap(); 295 } 296 297 /** 298 * getCollapsibleInputMap 299 * 300 * @return InputMap 301 */ getCollapsibleInputMap()302 public InputMap getCollapsibleInputMap() { 303 return myToggleCollapseButton.getInputMap(); 304 } 305 306 @Override paintComponent(Graphics graphics)307 protected void paintComponent(Graphics graphics) { 308 updatePanel(); 309 super.paintComponent(graphics); 310 } 311 updatePanel()312 private void updatePanel() { 313 if (paintAsSelected()) { 314 setBackground(UIUtil.getTableSelectionBackground(true)); 315 } else { 316 setBackground(myContent.getBackground()); 317 } 318 } 319 320 @Override paintChildren(Graphics graphics)321 protected void paintChildren(Graphics graphics) { 322 if (myTitleLabel != null) { 323 updateTitle(); 324 } 325 updateToggleButton(); 326 super.paintChildren(graphics); 327 } 328 updateToggleButton()329 private void updateToggleButton() { 330 if (paintAsSelected()) { 331 myToggleCollapseButton.setBackground(JBColor.background().brighter()); 332 } else { 333 myToggleCollapseButton.setBackground(myContent.getBackground()); 334 } 335 } 336 updateTitle()337 private void updateTitle() { 338 if (paintAsSelected()) { 339 myTitleLabel.setForeground(JBColor.foreground()); 340 myTitleLabel.setBackground(JBColor.background().brighter()); 341 } else { 342 myTitleLabel.setForeground(JBColor.foreground()); 343 myTitleLabel.setBackground(JBColor.background().brighter()); 344 } 345 } 346 paintAsSelected()347 private boolean paintAsSelected() { 348 return myToggleCollapseButton.hasFocus() && isCollapsed(); 349 } 350 351 /** 352 * CollapsingListener 353 * 354 * @since 2021/5/18 23:18 355 */ 356 public interface CollapsingListener { 357 /** 358 * onCollapsingChanged 359 * 360 * @param panel panel 361 * @param newValue newValue 362 */ onCollapsingChanged(@otNull FolderPanel panel, boolean newValue)363 void onCollapsingChanged(@NotNull FolderPanel panel, boolean newValue); 364 } 365 366 } 367