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.

This commit is contained in:
Yaroslav Lepenkin
2015-04-08 16:43:00 +03:00
parent 8f30a5836a
commit 0e9ea6151f
@@ -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<Point>() {
@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;
}
}
}
}