jetbrains.jetpad.projectional.domUtil.DomTextEditor.java Source code

Java tutorial

Introduction

Here is the source code for jetbrains.jetpad.projectional.domUtil.DomTextEditor.java

Source

/*
 * Copyright 2012-2016 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 jetbrains.jetpad.projectional.domUtil;

import com.google.common.base.Objects;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.user.client.DOM;
import jetbrains.jetpad.values.Color;
import jetbrains.jetpad.values.Font;
import jetbrains.jetpad.values.FontFamily;

public class DomTextEditor {
    public static final Font DEFAULT_FONT = new Font(FontFamily.MONOSPACED, 15);

    private static final TextMetrics ourDefaultFontMetrics = TextMetricsCalculator.calculate(DEFAULT_FONT);

    private int myCaretPosition;
    private int mySelectionStart;
    private boolean mySelectionVisible;
    private String myText;
    private boolean myCaretVisible;
    private Color myTextColor;

    private Font myFont = DEFAULT_FONT;
    private TextMetrics myFontMetrics = ourDefaultFontMetrics;

    private Element myTextContainer;
    private Element myCaretDiv;
    private Element mySelectionDiv;
    private Element myRoot;

    public DomTextEditor(Element root) {
        myRoot = root;

        Style rootStyle = myRoot.getStyle();
        rootStyle.setPosition(Style.Position.RELATIVE);

        myTextContainer = DOM.createSpan();
        Style textStyle = myTextContainer.getStyle();
        textStyle.setZIndex(2);
        textStyle.setWhiteSpace(Style.WhiteSpace.NOWRAP);
        myRoot.appendChild(myTextContainer);

        Element caretDiv = DOM.createDiv();
        Style caretStyle = caretDiv.getStyle();
        caretStyle.setPosition(Style.Position.ABSOLUTE);
        caretStyle.setZIndex(2);
        myRoot.appendChild(caretDiv);
        myCaretDiv = caretDiv;

        Element selectionDiv = DOM.createDiv();
        Style selectionStyle = selectionDiv.getStyle();
        selectionStyle.setPosition(Style.Position.ABSOLUTE);
        selectionStyle.setZIndex(1);
        myRoot.appendChild(selectionDiv);
        mySelectionDiv = selectionDiv;

        update();
        updateCaretVisibility();
        updateSelectionVisibility();
        updateCaretAndSelection();
    }

    public Font getFont() {
        return myFont;
    }

    public void setBold(boolean bold) {
        if (myFont.isBold() == bold)
            return;
        myFont = new Font(myFont.getFamily(), myFont.getSize(), bold, myFont.isItalic());
        fontChanged();
        updateBold();
    }

    public void setItalic(boolean italic) {
        if (myFont.isItalic() == italic)
            return;
        myFont = new Font(myFont.getFamily(), myFont.getSize(), myFont.isBold(), italic);
        fontChanged();
        updateItalic();
    }

    public void setFontFamily(FontFamily family) {
        if (Objects.equal(myFont.getFamily(), family))
            return;
        myFont = new Font(family, myFont.getSize(), myFont.isBold(), myFont.isItalic());
        fontChanged();
        updateFontFamily();
    }

    public void setFontSize(int size) {
        if (myFont.getSize() == size)
            return;
        myFont = new Font(myFont.getFamily(), size, myFont.isBold(), myFont.isItalic());
        fontChanged();
        updateFontSize();
        updateLineHeight();
        updateText();
    }

    private void fontChanged() {
        myFontMetrics = DEFAULT_FONT.equals(myFont) ? ourDefaultFontMetrics
                : TextMetricsCalculator.calculate(myFont);
    }

    public Color getTextColor() {
        return myTextColor;
    }

    public void setTextColor(Color textColor) {
        if (Objects.equal(textColor, myTextColor))
            return;
        myTextColor = textColor;
        updateColor();
    }

    public String getText() {
        return myText;
    }

    public void setText(String text) {
        if (Objects.equal(text, myText))
            return;
        myText = text;
        updateText();

        if (mySelectionVisible) {
            updateSelectionBoundsAndText();
        }
    }

    public int getCaretPosition() {
        return myCaretPosition;
    }

    public void setCaretPosition(int caretPosition) {
        if (myCaretPosition == caretPosition)
            return;
        myCaretPosition = caretPosition;
        updateCaretPosition();

        if (mySelectionVisible) {
            updateSelectionBoundsAndText();
        }
    }

    public boolean getSelectionVisible() {
        return mySelectionVisible;
    }

    public void setSelectionVisible(boolean visible) {
        if (mySelectionVisible == visible)
            return;
        mySelectionVisible = visible;
        updateSelectionVisibility();
        updateSelectionBoundsAndText();
    }

    public int getSelectionStart() {
        return mySelectionStart;
    }

    public void setSelectionStart(int selectionStart) {
        if (mySelectionStart == selectionStart)
            return;
        mySelectionStart = selectionStart;
        updateSelectionBoundsAndText();
    }

    public boolean isCaretVisible() {
        return myCaretVisible;
    }

    public void setCaretVisible(boolean caretVisible) {
        if (myCaretVisible == caretVisible)
            return;
        myCaretVisible = caretVisible;
        updateCaretVisibility();
    }

    private void updateCaretVisibility() {
        myCaretDiv.getStyle().setVisibility(myCaretVisible ? Style.Visibility.VISIBLE : Style.Visibility.HIDDEN);
    }

    private void updateSelectionVisibility() {
        mySelectionDiv.getStyle()
                .setVisibility(mySelectionVisible ? Style.Visibility.VISIBLE : Style.Visibility.HIDDEN);
    }

    private void updateCaretPosition() {
        myCaretDiv.getStyle().setLeft(getCaretOffset(myCaretPosition), Style.Unit.PX);
    }

    private void updateCaretAndSelection() {
        Style caretStyle = myCaretDiv.getStyle();
        updateCaretPosition();
        caretStyle.setTop(0, Style.Unit.PX);
        caretStyle.setWidth(1, Style.Unit.PX);
        caretStyle.setHeight(getLineHeight(), Style.Unit.PX);
        caretStyle.setBackgroundColor("black");

        Style selectionStyle = mySelectionDiv.getStyle();
        selectionStyle.setTop(0, Style.Unit.PX);
        selectionStyle.setHeight(getLineHeight(), Style.Unit.PX);
        selectionStyle.setBackgroundColor("Highlight");
        selectionStyle.setColor("HighlightText");

        updateSelectionBoundsAndText();
    }

    private void updateSelectionBoundsAndText() {
        int left = Math.min(myCaretPosition, mySelectionStart);
        int right = Math.max(myCaretPosition, mySelectionStart);

        String text = myText == null ? "" : myText;
        text = text.substring(left, right);

        Style selectionStyle = mySelectionDiv.getStyle();
        selectionStyle.setLeft(getCaretOffset(left), Style.Unit.PX);
        selectionStyle.setWidth(getCaretOffset(right) - getCaretOffset(left), Style.Unit.PX);

        mySelectionDiv.setInnerText(TextMetricsCalculator.normalize(text));
    }

    private void update() {
        updateColor();
        updateFontFamily();
        updateFontSize();
        updateBold();
        updateItalic();
        updateText();
        updateLineHeight();
    }

    private void updateColor() {
        myRoot.getStyle().setColor(myTextColor == null ? null : myTextColor.toCssColor());
    }

    private void updateBold() {
        myRoot.getStyle().setFontWeight(myFont.isBold() ? Style.FontWeight.BOLD : Style.FontWeight.NORMAL);
    }

    private void updateItalic() {
        myRoot.getStyle().setFontStyle(myFont.isItalic() ? Style.FontStyle.ITALIC : Style.FontStyle.NORMAL);
    }

    private void updateText() {
        if (myText == null || myText.isEmpty()) {
            myTextContainer.setInnerText(" ");
            myTextContainer.getStyle().setWidth(1, Style.Unit.PX);
            myRoot.getStyle().setHeight(getLineHeight(), Style.Unit.PX);
        } else {
            myRoot.getStyle().clearHeight();
            myTextContainer.getStyle().clearWidth();
            myTextContainer.setInnerText(TextMetricsCalculator.normalize(myText));
        }
    }

    private void updateFontFamily() {
        myRoot.getStyle().setProperty("fontFamily", TextMetricsCalculator.getFontName(myFont.getFamily()));
    }

    private void updateFontSize() {
        myRoot.getStyle().setFontSize(myFont.getSize(), Style.Unit.PX);
    }

    private void updateLineHeight() {
        myRoot.getStyle().setLineHeight(getLineHeight(), Style.Unit.PX);
    }

    public double getCaretOffset(int caretOffset) {
        if (caretOffset == 0)
            return 0;
        if (FontFamily.MONOSPACED == myFont.getFamily()) {
            return caretOffset * getCharWidth();
        } else {
            return TextMetricsCalculator.calculateWidth(myFont, myText.substring(0, caretOffset));
        }
    }

    private double getLineHeight() {
        return myFontMetrics.dimension().y;
    }

    private double getCharWidth() {
        return myFontMetrics.dimension().x;
    }

    public int getCaretPositionAt(int caretOffset) {
        String textValue = myText;
        if (caretOffset <= 0)
            return 0;
        if (textValue == null)
            return 0;

        if (FontFamily.MONOSPACED == myFont.getFamily()) {
            double charWidth = getCharWidth();
            double pos = caretOffset / charWidth;
            double tail = caretOffset % charWidth;
            if (tail > charWidth / 2) {
                pos += 1;
            }
            int len = textValue.length();
            return pos > len ? len : (int) pos;

        } else {
            for (int i = 0; i <= myText.length(); i++) {
                if (TextMetricsCalculator.calculateWidth(myFont, myText.substring(0, i)) >= caretOffset)
                    return i;
            }
            return myText.length();
        }
    }
}