mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-07-27 19:59:03 +02:00
Show correct caret shape on command line
Also refreshes font when editor font changes
This commit is contained in:
parent
25b11349a4
commit
e94eac77eb
src/com/maddyhome/idea/vim/ui
@ -20,10 +20,7 @@ package com.maddyhome.idea.vim.ui;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.text.AttributeSet;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.PlainDocument;
|
||||
import javax.swing.text.*;
|
||||
|
||||
/**
|
||||
* This document provides insert/overwrite mode
|
||||
@ -32,7 +29,7 @@ public class ExDocument extends PlainDocument {
|
||||
/**
|
||||
* Toggles the insert/overwrite state
|
||||
*/
|
||||
public void toggleInsertReplace() {
|
||||
void toggleInsertReplace() {
|
||||
overwrite = !overwrite;
|
||||
}
|
||||
|
||||
@ -41,7 +38,7 @@ public class ExDocument extends PlainDocument {
|
||||
*
|
||||
* @return true if overwrite, false if insert mode
|
||||
*/
|
||||
public boolean isOverwrite() {
|
||||
boolean isOverwrite() {
|
||||
return overwrite;
|
||||
}
|
||||
|
||||
@ -72,5 +69,19 @@ public class ExDocument extends PlainDocument {
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean overwrite = false;
|
||||
public char getCharacter(int offset) {
|
||||
// If we're a proportional font, 'o' is a good char to use. If we're fixed width, it's still a good char to use
|
||||
if (offset >= getLength())
|
||||
return 'o';
|
||||
|
||||
try {
|
||||
final Segment segment = new Segment();
|
||||
getContent().getChars(offset,1, segment);
|
||||
return segment.charAt(0);
|
||||
} catch (BadLocationException e) {
|
||||
return 'o';
|
||||
}
|
||||
}
|
||||
|
||||
private boolean overwrite = false;
|
||||
}
|
||||
|
@ -93,6 +93,7 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
|
||||
private void setFontForElements() {
|
||||
final Font font = UiHelper.getEditorFont();
|
||||
label.setFont(font);
|
||||
entry.setFont(font);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,13 +106,13 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
|
||||
* @param count A holder for the ex entry count
|
||||
*/
|
||||
public void activate(@NotNull Editor editor, DataContext context, @NotNull String label, String initText, int count) {
|
||||
entry.setEditor(editor, context);
|
||||
this.label.setText(label);
|
||||
this.count = count;
|
||||
setFontForElements();
|
||||
entry.setDocument(entry.createDefaultModel());
|
||||
entry.setEditor(editor, context);
|
||||
entry.setText(initText);
|
||||
entry.setType(label);
|
||||
entry.setInsertMode();
|
||||
parent = editor.getContentComponent();
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
JRootPane root = SwingUtilities.getRootPane(parent);
|
||||
|
@ -21,8 +21,6 @@ package com.maddyhome.idea.vim.ui;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.colors.EditorFontType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
@ -32,29 +30,33 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.Keymap;
|
||||
import javax.swing.text.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.FocusAdapter;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
|
||||
/**
|
||||
* Provides a custom keymap for the text field. The keymap is the VIM Ex command keymapping
|
||||
*/
|
||||
public class ExTextField extends JTextField {
|
||||
|
||||
ExTextField() {
|
||||
addFocusListener(new FocusListener() {
|
||||
CommandLineCaret caret = new CommandLineCaret();
|
||||
caret.setBlinkRate(getCaret().getBlinkRate());
|
||||
setCaret(caret);
|
||||
setNormalModeCaret();
|
||||
|
||||
addCaretListener(e -> resetCaret());
|
||||
|
||||
addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
setCaretPosition(getText().length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusLost(FocusEvent e) {
|
||||
setCaretPosition(getDocument().getLength());
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -78,9 +80,6 @@ public class ExTextField extends JTextField {
|
||||
public void updateUI() {
|
||||
super.updateUI();
|
||||
|
||||
Font font = EditorColorsManager.getInstance().getGlobalScheme().getFont(EditorFontType.PLAIN);
|
||||
setFont(font);
|
||||
|
||||
setBorder(null);
|
||||
|
||||
// Do not override getActions() method, because it is has side effect: propogates these actions to defaults.
|
||||
@ -98,7 +97,7 @@ public class ExTextField extends JTextField {
|
||||
setKeymap(map);
|
||||
}
|
||||
|
||||
public void setType(@NotNull String type) {
|
||||
void setType(@NotNull String type) {
|
||||
String hkey = null;
|
||||
switch (type.charAt(0)) {
|
||||
case '/':
|
||||
@ -116,11 +115,11 @@ public class ExTextField extends JTextField {
|
||||
}
|
||||
}
|
||||
|
||||
public void saveLastEntry() {
|
||||
void saveLastEntry() {
|
||||
lastEntry = getText();
|
||||
}
|
||||
|
||||
public void selectHistory(boolean isUp, boolean filter) {
|
||||
void selectHistory(boolean isUp, boolean filter) {
|
||||
int dir = isUp ? -1 : 1;
|
||||
if (histIndex + dir < 0 || histIndex + dir > history.size()) {
|
||||
VimPlugin.indicateError();
|
||||
@ -164,7 +163,28 @@ public class ExTextField extends JTextField {
|
||||
}
|
||||
}
|
||||
|
||||
private static final String vimExTextFieldDisposeKey = "vimExTextFieldDisposeKey";
|
||||
private void updateText(String string) {
|
||||
super.setText(string);
|
||||
}
|
||||
|
||||
public void setText(String string) {
|
||||
super.setText(string);
|
||||
|
||||
saveLastEntry();
|
||||
}
|
||||
|
||||
void setEditor(Editor editor, DataContext context) {
|
||||
this.editor = editor;
|
||||
this.context = context;
|
||||
String disposeKey = vimExTextFieldDisposeKey + editor.hashCode();
|
||||
Project project = editor.getProject();
|
||||
if (Disposer.get(disposeKey) == null && project != null) {
|
||||
Disposer.register(project, () -> {
|
||||
this.editor = null;
|
||||
this.context = null;
|
||||
}, disposeKey);
|
||||
}
|
||||
}
|
||||
|
||||
public Editor getEditor() {
|
||||
return editor;
|
||||
@ -193,16 +213,6 @@ public class ExTextField extends JTextField {
|
||||
super.processKeyEvent(event);
|
||||
}
|
||||
|
||||
public void updateText(String string) {
|
||||
super.setText(string);
|
||||
}
|
||||
|
||||
public void setText(String string) {
|
||||
super.setText(string);
|
||||
|
||||
saveLastEntry();
|
||||
}
|
||||
|
||||
protected void processKeyEvent(KeyEvent e) {
|
||||
if (logger.isDebugEnabled()) logger.debug("key=" + e);
|
||||
super.processKeyEvent(e);
|
||||
@ -229,150 +239,157 @@ public class ExTextField extends JTextField {
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurrentAction(@Nullable Action action) {
|
||||
void setCurrentAction(@Nullable Action action) {
|
||||
this.currentAction = action;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Action getCurrentAction() {
|
||||
Action getCurrentAction() {
|
||||
return currentAction;
|
||||
}
|
||||
|
||||
public void toggleInsertReplace() {
|
||||
void setInsertMode() {
|
||||
ExDocument doc = (ExDocument)getDocument();
|
||||
if (doc.isOverwrite()) {
|
||||
doc.toggleInsertReplace();
|
||||
}
|
||||
resetCaret();
|
||||
}
|
||||
|
||||
void toggleInsertReplace() {
|
||||
ExDocument doc = (ExDocument)getDocument();
|
||||
doc.toggleInsertReplace();
|
||||
|
||||
/*
|
||||
Caret caret;
|
||||
int width;
|
||||
if (doc.isOverwrite())
|
||||
{
|
||||
caret = blockCaret;
|
||||
width = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
caret = origCaret;
|
||||
width = 1;
|
||||
}
|
||||
|
||||
setCaret(caret);
|
||||
putClientProperty("caretWidth", new Integer(width));
|
||||
*/
|
||||
resetCaret();
|
||||
}
|
||||
|
||||
/*
|
||||
private static class BlockCaret extends DefaultCaret
|
||||
{
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
if(!isVisible())
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
Rectangle rectangle;
|
||||
TextUI textui = getComponent().getUI();
|
||||
rectangle = textui.modelToView(getComponent(), getDot(), Position.Bias.Forward);
|
||||
if (rectangle == null || rectangle.width == 0 && rectangle.height == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (width > 0 && height > 0 && !_contains(rectangle.x, rectangle.y, rectangle.width, rectangle.height))
|
||||
{
|
||||
Rectangle rectangle1 = g.getClipBounds();
|
||||
if (rectangle1 != null && !rectangle1.contains(this))
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
damage(rectangle);
|
||||
}
|
||||
g.setColor(getComponent().getCaretColor());
|
||||
int i = 8;
|
||||
//rectangle.x -= i >> 1;
|
||||
g.fillRect(rectangle.x, rectangle.y, i, rectangle.height - 1);
|
||||
Document document = getComponent().getDocument();
|
||||
if (document instanceof AbstractDocument)
|
||||
{
|
||||
Element element = ((AbstractDocument)document).getBidiRootElement();
|
||||
if (element != null && element.getElementCount() > 1)
|
||||
{
|
||||
int[] flagXPoints = new int[3];
|
||||
int[] flagYPoints = new int[3];
|
||||
flagXPoints[0] = rectangle.x + i;
|
||||
flagYPoints[0] = rectangle.y;
|
||||
flagXPoints[1] = flagXPoints[0];
|
||||
flagYPoints[1] = flagYPoints[0] + 4;
|
||||
flagXPoints[2] = flagXPoints[0] + 4;
|
||||
flagYPoints[2] = flagYPoints[0];
|
||||
g.fillPolygon(flagXPoints, flagYPoints, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (BadLocationException badlocationexception)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
private void resetCaret() {
|
||||
if (getCaretPosition() == getText().length()) {
|
||||
setNormalModeCaret();
|
||||
}
|
||||
else {
|
||||
ExDocument doc = (ExDocument)getDocument();
|
||||
if (doc.isOverwrite()) {
|
||||
setReplaceModeCaret();
|
||||
}
|
||||
|
||||
private boolean _contains(int i, int j, int k, int l)
|
||||
{
|
||||
int i1 = width;
|
||||
int j1 = height;
|
||||
if ((i1 | j1 | k | l) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int k1 = x;
|
||||
int l1 = y;
|
||||
if (i < k1 || j < l1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (k > 0)
|
||||
{
|
||||
i1 += k1;
|
||||
k += i;
|
||||
if (k <= i)
|
||||
{
|
||||
if (i1 >= k1 || k > i1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (i1 >= k1 && k > i1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (k1 + i1 < i)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (l > 0)
|
||||
{
|
||||
j1 += l1;
|
||||
l += j;
|
||||
if (l <= j)
|
||||
{
|
||||
if (j1 >= l1 || l > j1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (j1 >= l1 && l > j1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (l1 + j1 < j)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
else {
|
||||
setInsertModeCaret();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The default cursor shapes for command line are:
|
||||
// 'c' command-line normal is block
|
||||
// 'ci' command-line insert is ver25
|
||||
// 'cr' command-line replace is hor20
|
||||
// see :help 'guicursor'
|
||||
// Note that we can't easily support guicursor because we don't have arbitrary control over the IntelliJ editor caret
|
||||
private void setNormalModeCaret() {
|
||||
CommandLineCaret caret = (CommandLineCaret) getCaret();
|
||||
caret.setBlockMode();
|
||||
}
|
||||
|
||||
private void setInsertModeCaret() {
|
||||
CommandLineCaret caret = (CommandLineCaret) getCaret();
|
||||
caret.setMode(CommandLineCaret.CaretMode.VER, 25);
|
||||
}
|
||||
|
||||
private void setReplaceModeCaret() {
|
||||
CommandLineCaret caret = (CommandLineCaret) getCaret();
|
||||
caret.setMode(CommandLineCaret.CaretMode.HOR, 20);
|
||||
}
|
||||
|
||||
private static class CommandLineCaret extends DefaultCaret {
|
||||
|
||||
private CaretMode mode;
|
||||
private int blockPercentage = 100;
|
||||
|
||||
public enum CaretMode {
|
||||
BLOCK,
|
||||
VER,
|
||||
HOR
|
||||
}
|
||||
|
||||
void setBlockMode() {
|
||||
setMode(CaretMode.BLOCK, 100);
|
||||
}
|
||||
|
||||
void setMode(CaretMode mode, int blockPercentage) {
|
||||
|
||||
// Make sure damage gets updated for the old and new shape so the flashing works correctly
|
||||
updateDamage();
|
||||
this.mode = mode;
|
||||
this.blockPercentage = blockPercentage;
|
||||
updateDamage();
|
||||
}
|
||||
|
||||
private void updateDamage() {
|
||||
try {
|
||||
Rectangle r = getComponent().getUI().modelToView(getComponent(), getDot(), getDotBias());
|
||||
damage(r);
|
||||
}
|
||||
catch(BadLocationException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
if (!isVisible())
|
||||
return;
|
||||
|
||||
try {
|
||||
final JTextComponent component = getComponent();
|
||||
final Color color = g.getColor();
|
||||
|
||||
g.setColor(component.getBackground());
|
||||
g.setXORMode(component.getCaretColor());
|
||||
|
||||
// We have to use the deprecated version because we still support 1.8
|
||||
final Rectangle r = component.getUI().modelToView(component, getDot(), getDotBias());
|
||||
FontMetrics fm = g.getFontMetrics();
|
||||
final int blockHeight = fm.getHeight();
|
||||
r.setBounds(r.x, r.y, getBlockWidth(fm), getBlockHeight(blockHeight));
|
||||
g.fillRect(r.x, r.y + blockHeight - r.height, r.width, r.height);
|
||||
g.setPaintMode();
|
||||
g.setColor(color);
|
||||
}
|
||||
catch (BadLocationException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
protected synchronized void damage(Rectangle r) {
|
||||
if (r != null) {
|
||||
JTextComponent component = getComponent();
|
||||
Font font = component.getFont();
|
||||
FontMetrics fm = component.getFontMetrics(font);
|
||||
final int blockHeight = fm.getHeight();
|
||||
width = getBlockWidth(fm);
|
||||
height = getBlockHeight(blockHeight);
|
||||
x = r.x;
|
||||
y = r.y + blockHeight - height;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private int getBlockWidth(FontMetrics fm) {
|
||||
if (mode == CaretMode.VER) {
|
||||
// Don't show a proportional width of a proportional font
|
||||
final int fullWidth = fm.charWidth('o');
|
||||
return max(1, fullWidth * blockPercentage / 100);
|
||||
}
|
||||
|
||||
final char c = ((ExDocument)getComponent().getDocument()).getCharacter(getComponent().getCaretPosition());
|
||||
return fm.charWidth(c);
|
||||
}
|
||||
|
||||
private int getBlockHeight(int fullHeight) {
|
||||
if (mode == CaretMode.HOR) {
|
||||
return max(1, fullHeight * blockPercentage / 100);
|
||||
}
|
||||
return fullHeight;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private Editor editor;
|
||||
private DataContext context;
|
||||
@ -380,22 +397,7 @@ public class ExTextField extends JTextField {
|
||||
private List<HistoryGroup.HistoryEntry> history;
|
||||
private int histIndex = 0;
|
||||
@Nullable private Action currentAction;
|
||||
// TODO - support block cursor for overwrite mode
|
||||
//private Caret origCaret;
|
||||
//private Caret blockCaret;
|
||||
|
||||
private static final String vimExTextFieldDisposeKey = "vimExTextFieldDisposeKey";
|
||||
private static final Logger logger = Logger.getInstance(ExTextField.class.getName());
|
||||
|
||||
void setEditor(Editor editor, DataContext context) {
|
||||
this.editor = editor;
|
||||
this.context = context;
|
||||
String disposeKey = vimExTextFieldDisposeKey + editor.hashCode();
|
||||
Project project = editor.getProject();
|
||||
if (Disposer.get(disposeKey) == null && project != null) {
|
||||
Disposer.register(project, () -> {
|
||||
this.editor = null;
|
||||
this.context = null;
|
||||
}, disposeKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user