You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by gb...@apache.org on 2010/09/07 18:38:27 UTC

svn commit: r993420 - in /pivot/trunk: tests/src/org/apache/pivot/tests/text_area_test.bxml wtk/src/org/apache/pivot/wtk/skin/LabelSkin.java wtk/src/org/apache/pivot/wtk/skin/TextAreaSkin2.java

Author: gbrown
Date: Tue Sep  7 16:38:26 2010
New Revision: 993420

URL: http://svn.apache.org/viewvc?rev=993420&view=rev
Log:
Add initial rendering support to TextAreaSkin2; minor optimization to LabelSkin.

Modified:
    pivot/trunk/tests/src/org/apache/pivot/tests/text_area_test.bxml
    pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/LabelSkin.java
    pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextAreaSkin2.java

Modified: pivot/trunk/tests/src/org/apache/pivot/tests/text_area_test.bxml
URL: http://svn.apache.org/viewvc/pivot/trunk/tests/src/org/apache/pivot/tests/text_area_test.bxml?rev=993420&r1=993419&r2=993420&view=diff
==============================================================================
--- pivot/trunk/tests/src/org/apache/pivot/tests/text_area_test.bxml (original)
+++ pivot/trunk/tests/src/org/apache/pivot/tests/text_area_test.bxml Tue Sep  7 16:38:26 2010
@@ -30,20 +30,23 @@ limitations under the License.
     }
     </windowStateListeners>
 
-    <ScrollPane>
-        <TextArea2 bxml:id="textArea" text="@sample.txt">
-            <textAreaContentListeners>
-            <![CDATA[
-            function textChanged(textArea) {
-                java.lang.System.out.println(textArea.text + "\n-");
+    <BoxPane orientation="vertical" styles="{padding:8}">
+        <Border>
+            <TextArea2 bxml:id="textArea" text="@sample.txt" preferredWidth="320"
+                styles="{backgroundColor:'#eeeeee', wrapText:true}">
+                <textAreaContentListeners>
+                <![CDATA[
+                function textChanged(textArea) {
+                    java.lang.System.out.println(textArea.text + "\n-");
 
-                for (var i = 0, n = textArea.paragraphs.length; i < n; i++) {
-                    var paragraph = textArea.paragraphs.get(i);
-                    java.lang.System.out.println("[" + i + "] " + paragraph.offset);
+                    for (var i = 0, n = textArea.paragraphs.length; i < n; i++) {
+                        var paragraph = textArea.paragraphs.get(i);
+                        java.lang.System.out.println("[" + i + "] " + paragraph.offset);
+                    }
                 }
-            }
-            ]]>
-            </textAreaContentListeners>
-        </TextArea2>
-    </ScrollPane>
+                ]]>
+                </textAreaContentListeners>
+            </TextArea2>
+        </Border>
+    </BoxPane>
 </Window>

Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/LabelSkin.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/LabelSkin.java?rev=993420&r1=993419&r2=993420&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/LabelSkin.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/LabelSkin.java Tue Sep  7 16:38:26 2010
@@ -243,13 +243,19 @@ public class LabelSkin extends Component
 
                     int start = 0;
                     int i = 0;
+
+                    // NOTE We use a character iterator here only because it is the most
+                    // efficient way to measure the character bounds (as of Java 6, the version
+                    // of Font#getStringBounds() that takes a String performs a string copy,
+                    // whereas the version that takes a character iterator does not)
+                    StringCharacterIterator ci = new StringCharacterIterator(text);
                     while (i < n) {
                         char c = text.charAt(i);
                         if (Character.isWhitespace(c)) {
                             lastWhitespaceIndex = i;
                         }
 
-                        Rectangle2D characterBounds = font.getStringBounds(text, i, i + 1, fontRenderContext);
+                        Rectangle2D characterBounds = font.getStringBounds(ci, i, i + 1, fontRenderContext);
                         lineWidth += characterBounds.getWidth();
 
                         if (lineWidth > width
@@ -341,7 +347,7 @@ public class LabelSkin extends Component
                 float lineWidth = (float)textBounds.getWidth();
 
                 float x = 0;
-                switch(horizontalAlignment) {
+                switch (horizontalAlignment) {
                     case LEFT: {
                         x = padding.left;
                         break;

Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextAreaSkin2.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextAreaSkin2.java?rev=993420&r1=993419&r2=993420&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextAreaSkin2.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextAreaSkin2.java Tue Sep  7 16:38:26 2010
@@ -21,17 +21,23 @@ import java.awt.Font;
 import java.awt.Graphics2D;
 import java.awt.Rectangle;
 import java.awt.Transparency;
+import java.awt.font.FontRenderContext;
 import java.awt.font.GlyphVector;
+import java.awt.font.LineMetrics;
 import java.awt.geom.Area;
+import java.awt.geom.Rectangle2D;
+import java.util.Locale;
 
 import org.apache.pivot.collections.ArrayList;
 import org.apache.pivot.collections.Dictionary;
 import org.apache.pivot.collections.Sequence;
+import org.apache.pivot.text.CharSequenceCharacterIterator;
 import org.apache.pivot.wtk.ApplicationContext;
 import org.apache.pivot.wtk.Bounds;
 import org.apache.pivot.wtk.Component;
 import org.apache.pivot.wtk.Dimensions;
 import org.apache.pivot.wtk.GraphicsUtilities;
+import org.apache.pivot.wtk.HorizontalAlignment;
 import org.apache.pivot.wtk.Insets;
 import org.apache.pivot.wtk.Keyboard;
 import org.apache.pivot.wtk.Mouse;
@@ -54,13 +60,15 @@ public class TextAreaSkin2 extends Compo
     public class ParagraphView implements Visual, TextArea2.ParagraphListener {
         private TextArea2.Paragraph paragraph;
 
+        private int x = 0;
+        private int y = 0;
         private int width = 0;
         private int height = 0;
 
         private int breakWidth = Integer.MAX_VALUE;
 
         private boolean valid = false;
-        private ArrayList<GlyphVector> rows = new ArrayList<GlyphVector>();
+        private ArrayList<GlyphVector> glyphVectors = new ArrayList<GlyphVector>();
 
         public ParagraphView(TextArea2.Paragraph paragraph) {
             this.paragraph = paragraph;
@@ -102,11 +110,44 @@ public class TextAreaSkin2 extends Compo
 
         @Override
         public void paint(Graphics2D graphics) {
-            // Iterate over rows and paint each
+            // Draw the text
             // TODO Only paint visible glyphs
-            for (GlyphVector line : rows) {
-                // TODO
-                graphics.drawGlyphVector(line, 0, 0);
+            int width = getWidth();
+
+            if (glyphVectors.getLength() > 0) {
+                FontRenderContext fontRenderContext = Platform.getFontRenderContext();
+                LineMetrics lm = font.getLineMetrics("", fontRenderContext);
+                float ascent = lm.getAscent();
+
+                float y = 0;
+                for (int i = 0, n = glyphVectors.getLength(); i < n; i++) {
+                    GlyphVector glyphVector = glyphVectors.get(i);
+
+                    Rectangle2D textBounds = glyphVector.getLogicalBounds();
+                    float lineWidth = (float)textBounds.getWidth();
+
+                    float x = 0;
+                    switch (horizontalAlignment) {
+                        case LEFT: {
+                            x = margin.left;
+                            break;
+                        }
+
+                        case RIGHT: {
+                            x = width - (lineWidth + margin.right);
+                            break;
+                        }
+
+                        case CENTER: {
+                            x = (width - lineWidth) / 2;
+                            break;
+                        }
+                    }
+
+                    graphics.drawGlyphVector(glyphVector, x, y + ascent);
+
+                    y += textBounds.getHeight();
+                }
             }
         }
 
@@ -115,11 +156,77 @@ public class TextAreaSkin2 extends Compo
         }
 
         public void validate() {
+            // TODO Validate from invalid offset rather than 0
+
             if (!valid) {
-                // TODO Re-layout glyphs and recalculate size
-                rows = new ArrayList<GlyphVector>();
+                glyphVectors = new ArrayList<GlyphVector>();
                 width = 0;
                 height = 0;
+
+                // Re-layout glyphs and recalculate size
+                CharSequence characters = paragraph.getCharacters();
+                int n = characters.length();
+
+                if (n > 0) {
+                    FontRenderContext fontRenderContext = Platform.getFontRenderContext();
+
+                    float lineWidth = 0;
+                    int lastWhitespaceIndex = -1;
+
+                    int start = 0;
+                    int i = 0;
+
+                    // NOTE We use a character iterator here only because it is the most
+                    // efficient way to measure the character bounds (as of Java 6, the version
+                    // of Font#getStringBounds() that takes a String performs a string copy,
+                    // whereas the version that takes a character iterator does not)
+                    CharSequenceCharacterIterator ci = new CharSequenceCharacterIterator(characters);
+                    while (i < n) {
+                        char c = characters.charAt(i);
+                        if (Character.isWhitespace(c)) {
+                            lastWhitespaceIndex = i;
+                        }
+
+                        Rectangle2D characterBounds = font.getStringBounds(ci, i, i + 1, fontRenderContext);
+                        lineWidth += characterBounds.getWidth();
+
+                        if (lineWidth > breakWidth
+                            && lastWhitespaceIndex != -1) {
+                            i = lastWhitespaceIndex;
+
+                            lineWidth = 0;
+                            lastWhitespaceIndex = -1;
+
+                            // Append the current line
+                            if ((i - 1) - start >= 0) {
+                                CharSequenceCharacterIterator line =
+                                    new CharSequenceCharacterIterator(characters, start, i, start);
+                                GlyphVector glyphVector = font.createGlyphVector(fontRenderContext, line);
+                                glyphVectors.add(glyphVector);
+
+                                Rectangle2D textBounds = glyphVector.getLogicalBounds();
+                                width = Math.max(width, (int)Math.ceil(textBounds.getWidth()));
+                                height += textBounds.getHeight();
+                            }
+
+                            start = i + 1;
+                        }
+
+                        i++;
+                    }
+
+                    // Append the final line
+                    if ((i - 1) - start >= 0) {
+                        CharSequenceCharacterIterator line =
+                            new CharSequenceCharacterIterator(characters, start, i, start);
+                        GlyphVector glyphVector = font.createGlyphVector(fontRenderContext, line);
+                        glyphVectors.add(glyphVector);
+
+                        Rectangle2D textBounds = glyphVector.getLogicalBounds();
+                        width = Math.max(width, (int)Math.ceil(textBounds.getWidth()));
+                        height += textBounds.getHeight();
+                    }
+                }
             }
 
             valid = true;
@@ -215,8 +322,9 @@ public class TextAreaSkin2 extends Compo
     private Color selectionBackgroundColor;
     private Color inactiveSelectionColor;
     private Color inactiveSelectionBackgroundColor;
-    private Insets margin = new Insets(4);
-    private boolean wrapText = true;
+    private HorizontalAlignment horizontalAlignment;
+    private Insets margin;
+    private boolean wrapText;
 
     private ArrayList<ParagraphView> paragraphViews = new ArrayList<ParagraphView>();
 
@@ -230,6 +338,9 @@ public class TextAreaSkin2 extends Compo
         selectionBackgroundColor = Color.BLACK;
         inactiveSelectionColor = Color.LIGHT_GRAY;
         inactiveSelectionBackgroundColor = Color.BLACK;
+        horizontalAlignment = HorizontalAlignment.LEFT;
+        margin = new Insets(4);
+        wrapText = true;
     }
 
     @Override
@@ -293,20 +404,83 @@ public class TextAreaSkin2 extends Compo
 
     @Override
     public void layout() {
+        TextArea2 textArea = (TextArea2)getComponent();
         int width = getWidth();
 
+        int y = margin.top;
+
         for (ParagraphView paragraphView : paragraphViews) {
             paragraphView.setBreakWidth(width);
 
-            // TODO Set location
+            // Set location
+            switch (horizontalAlignment) {
+                case LEFT: {
+                    paragraphView.x = 0;
+                    break;
+                }
+
+                case RIGHT: {
+                    paragraphView.x = width - paragraphView.getWidth();
+                    break;
+                }
+
+                case CENTER: {
+                    paragraphView.x = (width - paragraphView.getWidth()) / 2;
+                    break;
+                }
+            }
+
+            paragraphView.y = y;
+            y += paragraphView.getHeight();
+        }
+
+        // TODO
+        // updateSelection();
+        caretX = caret.x;
+
+        if (textArea.isFocused()) {
+            scrollCharacterToVisible(textArea.getSelectionStart());
         }
+
+        showCaret(textArea.isFocused()
+            && textArea.getSelectionLength() == 0);
+    }
+
+    @Override
+    public int getBaseline(int width, int height) {
+        FontRenderContext fontRenderContext = Platform.getFontRenderContext();
+        LineMetrics lm = font.getLineMetrics("", fontRenderContext);
+
+        return Math.round(margin.top + lm.getAscent());
     }
 
     @Override
     public void paint(Graphics2D graphics) {
+        int width = getWidth();
+        int height = getHeight();
+
+        if (backgroundColor != null) {
+            graphics.setPaint(backgroundColor);
+            graphics.fillRect(0, 0, width, height);
+        }
+
         // TODO Paint selection state
-        // TODO Walk paragraph view list and translate graphics/paint each view (only
-        // paint visible views)
+
+        // Draw text
+        graphics.setFont(font);
+        graphics.setPaint(color);
+
+        graphics.translate(0, margin.top);
+
+        for (int i = 0, n = paragraphViews.getLength(); i < n; i++) {
+            ParagraphView paragraphView = paragraphViews.get(i);
+
+            graphics.translate(paragraphView.x, 0);
+            paragraphView.paint(graphics);
+            graphics.translate(-paragraphView.x, 0);
+
+            graphics.translate(0, paragraphView.getHeight());
+        }
     }
 
     @Override
@@ -337,6 +511,10 @@ public class TextAreaSkin2 extends Compo
 
     public Bounds getCharacterBounds(int index) {
         // TODO Check for paragraph terminators including implicit final terminator
+
+        // TODO Translate returned bounds based on horizontal alignment and view's
+        // y-coordinate
+
         return null;
     }
 
@@ -522,6 +700,27 @@ public class TextAreaSkin2 extends Compo
         setInactiveSelectionBackgroundColor(GraphicsUtilities.decodeColor(inactiveSelectionBackgroundColor));
     }
 
+    public HorizontalAlignment getHorizontalAlignment() {
+        return horizontalAlignment;
+    }
+
+    public void setHorizontalAlignment(HorizontalAlignment horizontalAlignment) {
+        if (horizontalAlignment == null) {
+            throw new IllegalArgumentException("horizontalAlignment is null.");
+        }
+
+        this.horizontalAlignment = horizontalAlignment;
+        repaintComponent();
+    }
+
+    public final void setHorizontalAlignment(String horizontalAlignment) {
+        if (horizontalAlignment == null) {
+            throw new IllegalArgumentException("horizontalAlignment is null.");
+        }
+
+        setHorizontalAlignment(HorizontalAlignment.valueOf(horizontalAlignment.toUpperCase(Locale.ENGLISH)));
+    }
+
     public Insets getMargin() {
         return margin;
     }
@@ -641,27 +840,43 @@ public class TextAreaSkin2 extends Compo
 
     @Override
     public void maximumLengthChanged(TextArea2 textArea, int previousMaximumLength) {
-        // TODO
+        // No-op
     }
 
     @Override
     public void editableChanged(TextArea2 textArea) {
-        // TODO
+        // No-op
     }
 
     @Override
     public void paragraphInserted(TextArea2 textArea, int index) {
-        // TODO Create paragraph view and add as paragraph listener
+        // Create paragraph view and add as paragraph listener
+        TextArea2.Paragraph paragraph = textArea.getParagraphs().get(index);
+        ParagraphView paragraphView = new ParagraphView(paragraph);
+        paragraph.getParagraphListeners().add(paragraphView);
+
+        // Insert view
+        paragraphViews.insert(paragraphView, index);
     }
 
     @Override
     public void paragraphsRemoved(TextArea2 textArea, int index, Sequence<TextArea2.Paragraph> removed) {
-        // TODO Remove paragraph views and remove as paragraph listeners
+        // Remove paragraph views as paragraph listeners
+        int count = removed.getLength();
+
+        for (int i = 0; i < count; i++) {
+            TextArea2.Paragraph paragraph = removed.get(i);
+            ParagraphView paragraphView = paragraphViews.get(i + index);
+            paragraph.getParagraphListeners().remove(paragraphView);
+        }
+
+        // Remove views
+        paragraphViews.remove(index, count);
     }
 
     @Override
     public void textChanged(TextArea2 textArea) {
-        // TODO
+        // No-op
     }
 
     @Override