From 0e9ea6151f29c304ceb3e86818782d33085b301f Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Fri, 3 Apr 2015 14:59:37 +0300 Subject: [PATCH] Calculating dialog location to prevent selected text hiding. We take first place from: on the right of the text, over selected text, under selected text, if dialog fits there. --- .../ConfigureCodeStyleOnSelectedFragment.java | 146 +++++++++++++++++- 1 file changed, 142 insertions(+), 4 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/contextConfiguration/ConfigureCodeStyleOnSelectedFragment.java b/platform/lang-impl/src/com/intellij/formatting/contextConfiguration/ConfigureCodeStyleOnSelectedFragment.java index a92e12bd2890..cbcb71b5aae9 100644 --- a/platform/lang-impl/src/com/intellij/formatting/contextConfiguration/ConfigureCodeStyleOnSelectedFragment.java +++ b/platform/lang-impl/src/com/intellij/formatting/contextConfiguration/ConfigureCodeStyleOnSelectedFragment.java @@ -18,11 +18,14 @@ package com.intellij.formatting.contextConfiguration; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.lang.Language; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.SelectionModel; +import com.intellij.openapi.editor.VisualPosition; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiFile; @@ -36,6 +39,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.awt.*; import static com.intellij.psi.codeStyle.CodeStyleSettingsCodeFragmentFilter.CodeStyleSettingsToShow; @@ -67,7 +71,7 @@ public class ConfigureCodeStyleOnSelectedFragment implements IntentionAction { SelectedTextFormatter textFormatter = new SelectedTextFormatter(project, editor, file); CodeStyleSettingsToShow settingsToShow = calculateAffectingSettings(editor, file); CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(project); - new FragmentCodeStyleSettingsDialog(project, textFormatter, file.getLanguage(), settings, settingsToShow).show(); + new FragmentCodeStyleSettingsDialog(editor, textFormatter, file.getLanguage(), settings, settingsToShow).show(); } private static CodeStyleSettingsToShow calculateAffectingSettings(@NotNull Editor editor, @NotNull PsiFile file) { @@ -85,21 +89,35 @@ public class ConfigureCodeStyleOnSelectedFragment implements IntentionAction { static class FragmentCodeStyleSettingsDialog extends DialogWrapper { private final CodeFragmentCodeStyleSettingsPanel myTabbedLanguagePanel; + private final Editor myEditor; + private final Document myDocument; private SelectedTextFormatter mySelectedTextFormatter; private final CodeStyleSettings mySettings; - public FragmentCodeStyleSettingsDialog(@NotNull Project project, + + public FragmentCodeStyleSettingsDialog(@NotNull final Editor editor, @NotNull SelectedTextFormatter selectedTextFormatter, @NotNull Language language, CodeStyleSettings settings, CodeStyleSettingsToShow settingsToShow) { - super(project, true); + super(editor.getContentComponent(), true); mySettings = settings; mySelectedTextFormatter = selectedTextFormatter; myTabbedLanguagePanel = new CodeFragmentCodeStyleSettingsPanel(settings, settingsToShow, language, selectedTextFormatter); - + + myEditor = editor; + myDocument = editor.getDocument(); + setTitle("Configure Code Style Settings: " + language.getDisplayName()); setOKButtonText("Save"); + + setInitialLocationCallback(new Computable() { + @Override + public Point compute() { + return new DialogPositionProvider().calculateLocation(); + } + }); + init(); } @@ -131,5 +149,125 @@ public class ConfigureCodeStyleOnSelectedFragment implements IntentionAction { mySelectedTextFormatter.restoreSelectedText(); super.doCancelAction(); } + + private class DialogPositionProvider { + private static final int PREFERRED_PADDING = 100; + + private JComponent myEditorComponent; + private JComponent myContentComponent; + + private int mySelectionStartY; + private int mySelectionEndY; + private int myTextRangeMaxColumnX; + private int myEditorComponentWidth; + private int myEditorComponentHeight; + + public DialogPositionProvider() { + myContentComponent = myEditor.getContentComponent(); + myEditorComponent = myEditor.getComponent(); + + myEditorComponentWidth = myEditorComponent.getWidth(); + myEditorComponentHeight = myEditorComponent.getHeight(); + } + + public Point calculateLocation() { + calculateSelectedTextRectangle(); + + int dialogWidth = getSize().width; + int dialogHeight = getSize().height; + + int spaceIfPlacedOnTheRight = myEditorComponentWidth - (myTextRangeMaxColumnX + dialogWidth); + + Integer dialogX = null; + Integer dialogY = null; + + if (spaceIfPlacedOnTheRight > 0) { + int paddingFromText = spaceIfPlacedOnTheRight > PREFERRED_PADDING ? PREFERRED_PADDING : (spaceIfPlacedOnTheRight / 2); + dialogX = myTextRangeMaxColumnX + paddingFromText; + + if (mySelectionEndY - mySelectionStartY > dialogHeight) { + dialogY = (myEditorComponentHeight - dialogHeight) / 2; + } + else { + dialogY = getYMatchingDialogAndSelectionCenter(dialogHeight); + } + } + else if (mySelectionStartY > dialogHeight) { + dialogX = (myEditorComponentWidth - dialogWidth) / 2; + dialogY = mySelectionStartY - dialogHeight; + } + else if (mySelectionEndY + dialogHeight < myEditorComponentHeight) { + dialogX = (myEditorComponentWidth - dialogWidth) / 2; + dialogY = mySelectionEndY; + } + + if (dialogX != null && dialogY != null) { + Point location = new Point(dialogX, dialogY); + SwingUtilities.convertPointToScreen(location, myEditor.getComponent()); + return location; + } + + return null; + } + + private Integer getYMatchingDialogAndSelectionCenter(int dialogHeight) { + int selectionCenter = (mySelectionStartY + mySelectionEndY) / 2; + int dialogTop = selectionCenter - dialogHeight / 2; + int dialogBottom = selectionCenter + dialogHeight / 2; + + if (dialogTop >= 0 && dialogBottom <= myEditorComponentHeight) { + return dialogTop; + } + else if (dialogTop < 0) { + int extraTopSpace = -dialogTop; + if (dialogBottom + extraTopSpace <= myEditorComponentHeight) { + return 0; + } + } + else if (dialogBottom > myEditorComponentHeight) { + int extraBottomSpace = dialogBottom - myEditorComponentHeight; + if (dialogTop - extraBottomSpace >= 0) { + return dialogTop - extraBottomSpace; + } + } + + return null; + } + + private void calculateSelectedTextRectangle() { + SelectionModel selectionModel = myEditor.getSelectionModel(); + int selectionStartOffset = selectionModel.getSelectionStart(); + int selectionEndOffset = selectionModel.getSelectionEnd(); + + VisualPosition maxColumnVp = getMaxColumnInsideRange(selectionStartOffset, selectionEndOffset); + + Point maxColumnsPoint = myEditor.visualPositionToXY(maxColumnVp); + Point selectionStart = myEditor.visualPositionToXY(myEditor.offsetToVisualPosition(selectionStartOffset)); + Point selectionEnd = myEditor.visualPositionToXY(myEditor.offsetToVisualPosition(selectionEndOffset)); + + selectionStart = SwingUtilities.convertPoint(myContentComponent, selectionStart, myEditorComponent); + selectionEnd = SwingUtilities.convertPoint(myContentComponent, selectionEnd, myEditorComponent); + maxColumnsPoint = SwingUtilities.convertPoint(myContentComponent, maxColumnsPoint, myEditorComponent); + + mySelectionStartY = selectionStart.y; + mySelectionEndY = selectionEnd.y; + myTextRangeMaxColumnX = maxColumnsPoint.x; + } + + private VisualPosition getMaxColumnInsideRange(int startOffset, int endOffset) { + int firstLine = myDocument.getLineNumber(startOffset); + int lastLine = myDocument.getLineNumber(endOffset); + + VisualPosition positionWithMaxColumn = new VisualPosition(0, 0); + for (int currentLine = firstLine; currentLine <= lastLine; currentLine++) { + int offset = myDocument.getLineEndOffset(currentLine); + VisualPosition position = myEditor.offsetToVisualPosition(offset); + if (position.getColumn() > positionWithMaxColumn.getColumn()) { + positionWithMaxColumn = position; + } + } + return positionWithMaxColumn; + } + } } }