1
0
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:
Matt Ellis 2019-04-23 16:49:20 +01:00
parent 25b11349a4
commit e94eac77eb
No known key found for this signature in database
GPG Key ID: FA6025D54131324B
3 changed files with 197 additions and 183 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}
}
}