diff --git a/platform/platform-api/src/com/intellij/ui/ExpandableTextField.java b/platform/platform-api/src/com/intellij/ui/ExpandableTextField.java new file mode 100644 index 000000000000..db01a6be58a5 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/ExpandableTextField.java @@ -0,0 +1,157 @@ +/* + * Copyright 2000-2017 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; + +import com.intellij.icons.AllIcons; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.components.JBScrollBar; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.util.Function; +import com.intellij.util.execution.ParametersListUtil; +import com.intellij.util.ui.JBInsets; +import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static com.intellij.openapi.keymap.KeymapUtil.getFirstKeyboardShortcutText; +import static java.awt.event.InputEvent.CTRL_MASK; +import static javax.swing.KeyStroke.getKeyStroke; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS; + +public final class ExpandableTextField extends JTextField implements Expandable { + private final Function parser; + private final Function joiner; + private JBPopup popup; + private String title; + + public ExpandableTextField() { + this(false); + } + + public ExpandableTextField(boolean colon) { + this(colon ? ParametersListUtil.COLON_LINE_PARSER : ParametersListUtil.DEFAULT_LINE_PARSER, + colon ? ParametersListUtil.COLON_LINE_JOINER : ParametersListUtil.DEFAULT_LINE_JOINER); + } + + public ExpandableTextField(@NotNull Function> parser, @NotNull Function, String> joiner) { + super(20); + putClientProperty("JTextField.variant", VARIANT); + UIUtil.addUndoRedoActions(this); + this.parser = text -> StringUtil.join(parser.fun(text), "\n"); + this.joiner = text -> joiner.fun(Arrays.asList(StringUtil.splitByLines(text))); + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + @Override + public void collapse() { + if (popup != null) popup.cancel(); + } + + @Override + public void expand() { + if (popup != null) return; + + Font font = getFont(); + FontMetrics metrics = font == null ? null : getFontMetrics(font); + int height = metrics == null ? 16 : metrics.getHeight(); + Dimension size = new Dimension(height * 32, height * 16); + + JTextArea area = new JTextArea(parser.fun(getText())); + area.setFont(font); + area.setCaretPosition(0); + area.setWrapStyleWord(true); + area.setLineWrap(true); + area.putClientProperty(Expandable.class, this); + UIUtil.addUndoRedoActions(area); + + JBScrollPane pane = new JBScrollPane(area); + pane.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_ALWAYS); + pane.getVerticalScrollBar().add(JBScrollBar.LEADING, new JLabel(AllIcons.General.CollapseComponent) {{ + String text = getFirstKeyboardShortcutText("CollapseExpandableComponent"); + setToolTipText(text.isEmpty() ? "Collapse" : "Collapse (" + text + ")"); + setBorder(JBUI.Borders.empty(5, 0, 1, 5)); + addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent event) { + setIcon(AllIcons.General.CollapseComponentHover); + } + + @Override + public void mouseExited(MouseEvent event) { + setIcon(AllIcons.General.CollapseComponent); + } + + @Override + public void mousePressed(MouseEvent event) { + collapse(); + } + }); + }}); + + Insets insets = getInsets(); + JBInsets.addTo(size, insets); + JBInsets.addTo(size, pane.getInsets()); + if (size.width < getWidth()) size.width = getWidth(); + pane.setPreferredSize(size); + pane.setViewportBorder(BorderFactory.createEmptyBorder(insets.top, insets.left, insets.bottom, insets.right)); + + popup = JBPopupFactory.getInstance() + .createComponentPopupBuilder(pane, area) + .setFocusable(true) + .setRequestFocus(true) + .setTitle(title) + .setLocateByContent(true) + .setCancelOnWindowDeactivation(false) + .setKeyboardActions(Collections.singletonList(Pair.create(event -> { + collapse(); + Window window = UIUtil.getWindow(this); + if (window != null) { + window.dispatchEvent(new KeyEvent(this, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), CTRL_MASK, KeyEvent.VK_ENTER, '\r')); + } + }, getKeyStroke(KeyEvent.VK_ENTER, CTRL_MASK)))) + .setCancelCallback(() -> { + try { + setText(joiner.fun(area.getText())); + setCaretPosition(0); + UIUtil.resetUndoRedoActions(this); + popup = null; + return true; + } + catch (Exception ignore) { + return false; + } + }).createPopup(); + popup.show(new RelativePoint(this, new Point(0, 0))); + } +} diff --git a/platform/platform-api/src/com/intellij/ui/RawCommandLineEditor.java b/platform/platform-api/src/com/intellij/ui/RawCommandLineEditor.java index 2a8402381e3c..55b46e2861cc 100644 --- a/platform/platform-api/src/com/intellij/ui/RawCommandLineEditor.java +++ b/platform/platform-api/src/com/intellij/ui/RawCommandLineEditor.java @@ -21,6 +21,8 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.ui.LabeledComponent; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Function; import com.intellij.util.execution.ParametersListUtil; import org.jetbrains.annotations.Nullable; @@ -35,6 +37,7 @@ import java.util.List; public class RawCommandLineEditor extends JPanel implements TextAccessor { private static final Logger LOG = Logger.getInstance("#com.intellij.ui.RawCommandLineEditor"); + private final JTextField myEditor; private final TextFieldWithBrowseButton myTextField; private String myDialogCaption = ""; @@ -44,27 +47,35 @@ public class RawCommandLineEditor extends JPanel implements TextAccessor { public RawCommandLineEditor(final Function> lineParser, final Function, String> lineJoiner) { super(new BorderLayout()); - myTextField = new TextFieldWithBrowseButton(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (myDialogCaption == null) { - Container parent = getParent(); - if (parent instanceof LabeledComponent) { - parent = parent.getParent(); + if (Registry.is("raw.command.line.editor.dialog")) { + myTextField = new TextFieldWithBrowseButton(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (myDialogCaption == null) { + Container parent = getParent(); + if (parent instanceof LabeledComponent) { + parent = parent.getParent(); + } + LOG.error("Did not call RawCommandLineEditor.setDialogCaption() in " + parent); + myDialogCaption = "Parameters"; } - LOG.error("Did not call RawCommandLineEditor.setDialogCaption() in " + parent); - myDialogCaption = "Parameters"; + Messages.showTextAreaDialog(myTextField.getTextField(), myDialogCaption, "EditParametersPopupWindow", lineParser, lineJoiner); } - Messages.showTextAreaDialog(myTextField.getTextField(), myDialogCaption, "EditParametersPopupWindow", lineParser, lineJoiner); - } - }); - myTextField.setButtonIcon(AllIcons.Actions.ShowViewer); - add(myTextField, BorderLayout.CENTER); + }); + myEditor = myTextField.getTextField(); + myTextField.setButtonIcon(AllIcons.Actions.ShowViewer); + add(myTextField, BorderLayout.CENTER); + } + else { + myTextField = null; + myEditor = new ExpandableTextField(lineParser, lineJoiner); + add(myEditor, BorderLayout.CENTER); + } setDescriptor(null); } public void setDescriptor(FileChooserDescriptor descriptor) { - InsertPathAction.addTo(myTextField.getTextField(), descriptor); + InsertPathAction.addTo(myEditor, descriptor); } public String getDialogCaption() { @@ -73,33 +84,37 @@ public class RawCommandLineEditor extends JPanel implements TextAccessor { public void setDialogCaption(String dialogCaption) { myDialogCaption = dialogCaption != null ? dialogCaption : ""; + if (myEditor instanceof ExpandableTextField) { + ExpandableTextField expandable = (ExpandableTextField)myEditor; + expandable.setTitle(myDialogCaption); + } } @Override public void setText(@Nullable String text) { - myTextField.setText(text); + myEditor.setText(text); } @Override public String getText() { - return myTextField.getText(); + return StringUtil.notNullize(myEditor.getText()); } public JTextField getTextField() { - return myTextField.getTextField(); + return myEditor; } public Document getDocument() { - return myTextField.getTextField().getDocument(); + return myEditor.getDocument(); } public void attachLabel(JLabel label) { - label.setLabelFor(myTextField.getTextField()); + label.setLabelFor(myEditor); } @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); - myTextField.setEnabled(enabled); + (myTextField != null ? myTextField : myEditor).setEnabled(enabled); } } diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 2e62307f62e5..4c56b9fa886b 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -688,6 +688,9 @@ emmet.segments.limit=50 emmet.template.length.limit.kilobytes=15 command.line.execution.timeout=30 +raw.command.line.editor.dialog=false +raw.command.line.editor.dialog.description=Use dialog to edit command line + ide.settings.keymap.input.method.enabled=false ide.settings.keymap.input.method.enabled.description=Use input method instead of simple key event to enter shortcuts.