Package limelight.ui.model

Source Code of limelight.ui.model.TextPanel

//- Copyright © 2008-2011 8th Light, Inc. All Rights Reserved.
//- Limelight and all included source files are distributed under terms of the MIT License.

package limelight.ui.model;

import limelight.Log;
import limelight.styles.RichStyle;
import limelight.styles.ScreenableStyle;
import limelight.styles.StyleAttribute;
import limelight.styles.StyleObserver;
import limelight.styles.abstrstyling.StyleValue;
import limelight.ui.text.StyledText;
import limelight.ui.text.StyledTextParser;
import limelight.util.Box;
import limelight.util.Util;

import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class TextPanel extends PanelBase implements StyleObserver, TextAccessor
{
  private String text;
  private Prop panel;
  private double consumedHeight;
  private double consumedWidth;
  private Graphics2D graphics;
  private boolean textChanged;
  public static FontRenderContext staticFontRenderingContext;
  private Box consumableArea;

  private LinkedList<TextLayout> lines;
  private List<StyledText> textChunks;

  //TODO MDM panel is not really needed here.  It's the same as parent.
  public TextPanel(Prop panel)
  {
    this.panel = panel;
  }

  public String getText()
  {
    return text;
  }

  public void setText(String text, Prop panel)
  {
    boolean differentText = !Util.equal(text, this.text);
//    if(!needsLayout() && differentText)
//      markAsNeedingLayout();
    this.text = text;
    if(differentText)
    {
      markAsNeedingLayout();
      doPropagateSizeChangeUp(getParent());
      getParent().markAsNeedingLayout();
    }
  }

  public void paintOn(Graphics2D graphics)
  {
    graphics.setColor(getStyle().getCompiledTextColor().getColor());
    float y = 0;
    if(lines == null)
      return;
    synchronized(this)
    {
      for(TextLayout textLayout : lines)
      {
        y += textLayout.getAscent();
        int x = getStyle().getCompiledHorizontalAlignment().getX((int) widthOf(textLayout), new Box(0, 0, getWidth(), getHeight()));
        textLayout.draw(graphics, x, y);
        y += textLayout.getDescent() + textLayout.getLeading();
      }
    }
  }

  public ScreenableStyle getStyle()
  {
    return panel.getStyle();
  }

  public Layout getDefaultLayout()
  {
    return TextPanelLayout.instance;
  }

  public void compile()
  {
    cleanup();
    buildLines();
    calculateDimensions();
    flushChanges();
    snapToSize();
    markAsDirty();
  }

  private void cleanup()
  {
    if(textChunks == null)
      return;

    for(StyledText chunk : textChunks)
      chunk.teardownStyles();

    textChunks = null;
  }

  public void snapToSize()
  {
    setSize((int) (consumedWidth + 0.5), (int) (consumedHeight + 0.5));
  }

  public synchronized void buildLines()
  {
    consumableArea = panel.getChildConsumableBounds();
    lines = new LinkedList<TextLayout>();

    if(text != null && text.length() > 0)
    {
      StyledTextParser parser = new StyledTextParser();
      textChunks = parser.parse(text);

      final Scene root = getRoot();
      if(root != null) // TODO MDM - It happens.... but how?  Ah!  Need to acquire tree lock when removing panels.
      {
        Map<String, RichStyle> styleMap = root.getStyles();
        for(StyledText styledText : textChunks)
          styledText.setupStyles(styleMap, getStyle(), this);
        // TODO MDM StyleObservers may cause a memory leak.  Styles keep track of panels that are no longer used?

        addLines();
      }
    }
  }

  private synchronized void addLines()
  {
    AttributedString aText = prepareAttributedString();
    AttributedCharacterIterator styledTextIterator = aText.getIterator();

    List<Integer> newlineLocations = getNewlineLocations(styledTextIterator);
    LineBreakMeasurer lbm = new LineBreakMeasurer(styledTextIterator, getRenderContext());

    float width = (float) consumableArea.width;
    if(width <= 0)
      return;

    TextLayout layout;
    int startOfNextLayout;

    int currentLine = 0;
    int endIndex = styledTextIterator.getEndIndex();

    do
    {
      if(currentLine < newlineLocations.size())
        startOfNextLayout = newlineLocations.get(currentLine) + 1;
      else
        startOfNextLayout = endIndex + 1;

      layout = lbm.nextLayout(width, startOfNextLayout, false);

      lines.add(layout);

      if(lbm.getPosition() == startOfNextLayout)
        currentLine += 1;
    }
    while(layout != null && lbm.getPosition() < endIndex);
  }

  private List<Integer> getNewlineLocations(AttributedCharacterIterator styledTextIterator)
  {
    List<Integer> newlineLocations = new ArrayList<Integer>();
    for(char c = styledTextIterator.first(); c != AttributedCharacterIterator.DONE; c = styledTextIterator.next())
    {
      if(c == '\n')
        newlineLocations.add(styledTextIterator.getIndex());
    }
    return newlineLocations;
  }

  private AttributedString prepareAttributedString()
  {
    List<StyledText> textChunks = getTextChunks();

    StringBuilder buf = new StringBuilder();

    for(StyledText textChunk : textChunks)
    {
      buf.append(textChunk.getText());
    }

    AttributedString attributedString = new AttributedString(buf.toString());

    int startIndex = 0;
    int endIndex = 0;

    for(StyledText textChunk : textChunks)
    {
      if(textChunk == textChunks.get(textChunks.size() - 1))
        endIndex = buf.length();
      else
        endIndex = startIndex + textChunk.getText().length();

      attributedString.addAttribute(TextAttribute.FONT, textChunk.getFont(), startIndex, endIndex);
      attributedString.addAttribute(TextAttribute.FOREGROUND, textChunk.getColor(), startIndex, endIndex);

      startIndex += textChunk.getText().length();
    }

    return attributedString;
  }

  public List<StyledText> getTextChunks()
  {
    return textChunks;
  }

  public static FontRenderContext getRenderContext()
  {
    if(staticFontRenderingContext == null)
    {
      AffineTransform affineTransform = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().getDefaultTransform();
      staticFontRenderingContext = new FontRenderContext(affineTransform, true, false);
    }
    return staticFontRenderingContext;
  }

  private void calculateDimensions()
  {
    consumedHeight = 0;
    consumedWidth = 0;
    synchronized(this)
    {
      for(TextLayout layout : lines)
      {
        consumedHeight += (layout.getAscent() + layout.getDescent() + layout.getLeading());
        double lineWidth = widthOf(layout);
        if(lineWidth > consumedWidth)
          consumedWidth = lineWidth;
      }
    }
  }

  private double widthOf(TextLayout layout)
  {
    return layout.getBounds().getWidth() + layout.getBounds().getX();
  }

  public void setGraphics(Graphics graphics)
  {
    this.graphics = (Graphics2D) graphics;
  }

  public boolean textChanged()
  {
    return textChanged;
  }

  public void flushChanges()
  {
    textChanged = false;
  }

  public Graphics2D getGraphics()
  {
    if(graphics != null)
      return graphics;
    else
      return panel.getGraphics();
  }

  public String toString()
  {
    return "TextPanel: <" + getText() + ">";
  }

  public boolean canBeBuffered()
  {
    return false;
  }

  public List<TextLayout> getLines()
  {
    return lines;
  }

  public void styleChanged(StyleAttribute attribute, StyleValue value)
  {
    markAsNeedingLayout();
    getParent().markAsNeedingLayout();
    doPropagateSizeChangeUp(getParent().getParent());
  }
}
TOP

Related Classes of limelight.ui.model.TextPanel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.