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 2009/10/26 19:45:15 UTC

svn commit: r829896 - in /incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk: TextArea.java skin/TextAreaSkin.java skin/terra/TerraTextAreaSkin.java skin/terra/TerraTheme.java

Author: gbrown
Date: Mon Oct 26 18:45:15 2009
New Revision: 829896

URL: http://svn.apache.org/viewvc?rev=829896&view=rev
Log:
Add preliminary support for displaying selection state in TextArea.

Added:
    incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTextAreaSkin.java
Modified:
    incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TextArea.java
    incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextAreaSkin.java
    incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTheme.java

Modified: incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TextArea.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TextArea.java?rev=829896&r1=829895&r2=829896&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TextArea.java (original)
+++ incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TextArea.java Mon Oct 26 18:45:15 2009
@@ -68,6 +68,24 @@
         public int getNextInsertionPoint(int x, int from, Direction direction);
 
         /**
+         * Returns the row index of the character at a given offset within the document.
+         *
+         * @param offset
+         *
+         * @return
+         * The row index of the character at the given offset.
+         */
+        public int getRowIndex(int offset);
+
+        /**
+         * Returns the total number of rows in the document.
+         *
+         * @return
+         * The number of rows in the document.
+         */
+        public int getRowCount();
+
+        /**
          * Returns the bounds of the character at a given offset within the
          * document.
          *
@@ -596,6 +614,16 @@
         return textAreaSkin.getNextInsertionPoint(x, from, direction);
     }
 
+    public int getRowIndex(int offset) {
+        TextArea.Skin textAreaSkin = (TextArea.Skin)getSkin();
+        return textAreaSkin.getRowIndex(offset);
+    }
+
+    public int getRowCount() {
+        TextArea.Skin textAreaSkin = (TextArea.Skin)getSkin();
+        return textAreaSkin.getRowCount();
+    }
+
     public Bounds getCharacterBounds(int offset) {
         TextArea.Skin textAreaSkin = (TextArea.Skin)getSkin();
         return textAreaSkin.getCharacterBounds(offset);

Modified: incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextAreaSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextAreaSkin.java?rev=829896&r1=829895&r2=829896&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextAreaSkin.java (original)
+++ incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextAreaSkin.java Mon Oct 26 18:45:15 2009
@@ -25,6 +25,7 @@
 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.text.CharacterIterator;
 import java.util.Iterator;
@@ -39,6 +40,7 @@
 import org.apache.pivot.wtk.Cursor;
 import org.apache.pivot.wtk.Dimensions;
 import org.apache.pivot.wtk.Direction;
+import org.apache.pivot.wtk.GraphicsUtilities;
 import org.apache.pivot.wtk.Insets;
 import org.apache.pivot.wtk.Keyboard;
 import org.apache.pivot.wtk.Mouse;
@@ -220,6 +222,8 @@
         public abstract NodeView getNext();
         public abstract int getInsertionPoint(int x, int y);
         public abstract int getNextInsertionPoint(int x, int from, Direction direction);
+        public abstract int getRowIndex(int offset);
+        public abstract int getRowCount();
         public abstract Bounds getCharacterBounds(int offset);
 
         @Override
@@ -543,6 +547,37 @@
         }
 
         @Override
+        public int getRowIndex(int offset) {
+            int rowIndex = 0;
+
+            for (NodeView nodeView : this) {
+                int nodeViewOffset = nodeView.getOffset();
+                int characterCount = nodeView.getCharacterCount();
+
+                if (offset >= nodeViewOffset
+                    && offset < nodeViewOffset + characterCount) {
+                    rowIndex += nodeView.getRowIndex(offset - nodeView.getOffset());
+                    break;
+                }
+
+                rowIndex += nodeView.getRowCount();
+            }
+
+            return rowIndex;
+        }
+
+        @Override
+        public int getRowCount() {
+            int rowCount = 0;
+
+            for (NodeView nodeView : this) {
+                rowCount += nodeView.getRowCount();
+            }
+
+            return rowCount;
+        }
+
+        @Override
         public NodeView getNext() {
             return null;
         }
@@ -573,7 +608,7 @@
         }
 
         private ArrayList<Row> rows = null;
-        private Bounds terminatorBounds = null;
+        private Bounds terminatorBounds = new Bounds(0, 0, 0, 0);
 
         public ParagraphView(Paragraph paragraph) {
             super(paragraph);
@@ -813,6 +848,32 @@
         }
 
         @Override
+        public int getRowIndex(int offset) {
+            int rowIndex = -1;
+
+            for (int i = 0, n = rows.getLength(); i < n; i++) {
+                Row row = rows.get(i);
+                NodeView firstNodeView = row.nodeViews.get(0);
+                NodeView lastNodeView = row.nodeViews.get(row.nodeViews.getLength() - 1);
+
+                if (offset >= firstNodeView.getOffset()
+                    && offset < lastNodeView.getOffset() + lastNodeView.getCharacterCount()) {
+                    rowIndex = i;
+                    break;
+                }
+
+                i++;
+            }
+
+            return rowIndex;
+        }
+
+        @Override
+        public int getRowCount() {
+            return rows.getLength();
+        }
+
+        @Override
         public Bounds getCharacterBounds(int offset) {
             Bounds bounds;
 
@@ -1027,6 +1088,16 @@
         }
 
         @Override
+        public int getRowIndex(int offset) {
+            return -1;
+        }
+
+        @Override
+        public int getRowCount() {
+            return 0;
+        }
+
+        @Override
         public Bounds getCharacterBounds(int offset) {
             Shape glyphLogicalBounds = glyphVector.getGlyphLogicalBounds(offset);
             Rectangle2D bounds2D = glyphLogicalBounds.getBounds2D();
@@ -1121,6 +1192,16 @@
         }
 
         @Override
+        public int getRowIndex(int offset) {
+            return -1;
+        }
+
+        @Override
+        public int getRowCount() {
+            return 0;
+        }
+
+        @Override
         public Bounds getCharacterBounds(int offset) {
             return new Bounds(0, 0, getWidth(), getHeight());
         }
@@ -1144,17 +1225,21 @@
         }
 
         public void regionUpdated(Image image, int x, int y, int width, int height) {
-            // TODO Repaint the corresponding area of the component
+            // TODO Repaint the corresponding area of the component (add a repaint()
+            // method to NodeView to facilitate this as well as paint-only updates
+            // such as color changes)
         }
     }
 
-    private class BlinkCursorCallback implements Runnable {
+    private class BlinkCaretCallback implements Runnable {
         @Override
         public void run() {
             caretOn = !caretOn;
 
-            TextArea textArea = (TextArea)getComponent();
-            textArea.repaint(caret.x, caret.y, caret.width, caret.height, true);
+            if (caret != null) {
+                TextArea textArea = (TextArea)getComponent();
+                textArea.repaint(caret.x, caret.y, caret.width, caret.height, true);
+            }
         }
     }
 
@@ -1163,12 +1248,20 @@
 
     private int caretX = 0;
     private Rectangle caret = new Rectangle();
+    private Area selection = null;
+
     private boolean caretOn = false;
-    private BlinkCursorCallback blinkCursorCallback = new BlinkCursorCallback();
+    private BlinkCaretCallback blinkCursorCallback = new BlinkCaretCallback();
     private ApplicationContext.ScheduledCallback scheduledBlinkCursorCallback = null;
 
     private Font font;
     private Color color;
+    private Color backgroundColor;
+    private Color selectionColor;
+    private Color selectionBackgroundColor;
+    private Color inactiveSelectionColor;
+    private Color inactiveSelectionBackgroundColor;
+
     private Insets margin = new Insets(4);
 
     public static final int PARAGRAPH_TERMINATOR_WIDTH = 2;
@@ -1178,6 +1271,10 @@
         Theme theme = Theme.getTheme();
         font = theme.getFont();
         color = Color.BLACK;
+        selectionColor = Color.LIGHT_GRAY;
+        selectionBackgroundColor = Color.BLACK;
+        inactiveSelectionColor = Color.LIGHT_GRAY;
+        inactiveSelectionBackgroundColor = Color.BLACK;
     }
 
     @Override
@@ -1194,7 +1291,7 @@
         if (document != null) {
             documentView = (DocumentView)createNodeView(document);
             documentView.attach();
-            updateCaretBounds();
+            updateSelection();
         }
     }
 
@@ -1243,24 +1340,38 @@
             documentView.setBreakWidth(Math.max(width - (margin.left + margin.right), 0));
             documentView.validate();
 
-            updateCaretBounds();
+            updateSelection();
         }
     }
 
     @Override
     public void paint(Graphics2D graphics) {
+        TextArea textArea = (TextArea)getComponent();
+        int width = getWidth();
+        int height = getHeight();
+
+        if (backgroundColor != null) {
+            graphics.setColor(backgroundColor);
+            graphics.fillRect(0, 0, width, height);
+        }
+
         if (documentView != null) {
-            TextArea textArea = (TextArea)getComponent();
+            // Draw the selection highlight
+            if (selection != null) {
+                graphics.setColor(textArea.isFocused() ?
+                    selectionBackgroundColor : inactiveSelectionBackgroundColor);
+                graphics.fill(selection);
+            }
 
+            // Draw the document content
             graphics.translate(margin.left, margin.top);
             documentView.paint(graphics);
             graphics.translate(-margin.left, -margin.top);
 
-            // TODO Paint selection state
-
-            if (textArea.getSelectionLength() == 0
-                && textArea.isFocused()
-                && caretOn) {
+            // Draw the caret
+            if (caret != null
+                && caretOn
+                && textArea.isFocused()) {
                 graphics.setPaint(Color.BLACK);
                 graphics.fill(caret);
             }
@@ -1298,6 +1409,32 @@
     }
 
     @Override
+    public int getRowIndex(int offset) {
+        int rowIndex;
+
+        if (documentView == null) {
+            rowIndex = -1;
+        } else {
+            rowIndex = documentView.getRowIndex(offset);
+        }
+
+        return rowIndex;
+    }
+
+    @Override
+    public int getRowCount() {
+        int rowCount;
+
+        if (documentView == null) {
+            rowCount = 0;
+        } else {
+            rowCount = documentView.getRowCount();
+        }
+
+        return rowCount;
+    }
+
+    @Override
     public Bounds getCharacterBounds(int offset) {
         Bounds characterBounds;
 
@@ -1314,6 +1451,44 @@
         return characterBounds;
     }
 
+    public Color getColor() {
+        return color;
+    }
+
+    public void setColor(Color color) {
+        if (color == null) {
+            throw new IllegalArgumentException("color is null.");
+        }
+
+        this.color = color;
+        repaintComponent();
+    }
+
+    public final void setColor(String color) {
+        if (color == null) {
+            throw new IllegalArgumentException("color is null.");
+        }
+
+        setColor(GraphicsUtilities.decodeColor(color));
+    }
+
+    public Color getBackgroundColor() {
+        return backgroundColor;
+    }
+
+    public void setBackgroundColor(Color backgroundColor) {
+        this.backgroundColor = backgroundColor;
+        repaintComponent();
+    }
+
+    public final void setBackgroundColor(String backgroundColor) {
+        if (backgroundColor == null) {
+            throw new IllegalArgumentException("backgroundColor is null");
+        }
+
+        setBackgroundColor(GraphicsUtilities.decodeColor(backgroundColor));
+    }
+
     public Font getFont() {
         return font;
     }
@@ -1343,6 +1518,90 @@
         setFont(Theme.deriveFont(font));
     }
 
+    public Color getSelectionColor() {
+        return selectionColor;
+    }
+
+    public void setSelectionColor(Color selectionColor) {
+        if (selectionColor == null) {
+            throw new IllegalArgumentException("selectionColor is null.");
+        }
+
+        this.selectionColor = selectionColor;
+        repaintComponent();
+    }
+
+    public final void setSelectionColor(String selectionColor) {
+        if (selectionColor == null) {
+            throw new IllegalArgumentException("selectionColor is null.");
+        }
+
+        setSelectionColor(GraphicsUtilities.decodeColor(selectionColor));
+    }
+
+    public Color getSelectionBackgroundColor() {
+        return selectionBackgroundColor;
+    }
+
+    public void setSelectionBackgroundColor(Color selectionBackgroundColor) {
+        if (selectionBackgroundColor == null) {
+            throw new IllegalArgumentException("selectionBackgroundColor is null.");
+        }
+
+        this.selectionBackgroundColor = selectionBackgroundColor;
+        repaintComponent();
+    }
+
+    public final void setSelectionBackgroundColor(String selectionBackgroundColor) {
+        if (selectionBackgroundColor == null) {
+            throw new IllegalArgumentException("selectionBackgroundColor is null.");
+        }
+
+        setSelectionBackgroundColor(GraphicsUtilities.decodeColor(selectionBackgroundColor));
+    }
+
+    public Color getInactiveSelectionColor() {
+        return inactiveSelectionColor;
+    }
+
+    public void setInactiveSelectionColor(Color inactiveSelectionColor) {
+        if (inactiveSelectionColor == null) {
+            throw new IllegalArgumentException("inactiveSelectionColor is null.");
+        }
+
+        this.inactiveSelectionColor = inactiveSelectionColor;
+        repaintComponent();
+    }
+
+    public final void setInactiveSelectionColor(String inactiveSelectionColor) {
+        if (inactiveSelectionColor == null) {
+            throw new IllegalArgumentException("inactiveSelectionColor is null.");
+        }
+
+        setInactiveSelectionColor(GraphicsUtilities.decodeColor(inactiveSelectionColor));
+    }
+
+    public Color getInactiveSelectionBackgroundColor() {
+        return inactiveSelectionBackgroundColor;
+    }
+
+    public void setInactiveSelectionBackgroundColor(Color inactiveSelectionBackgroundColor) {
+        if (inactiveSelectionBackgroundColor == null) {
+            throw new IllegalArgumentException("inactiveSelectionBackgroundColor is null.");
+        }
+
+        this.inactiveSelectionBackgroundColor = inactiveSelectionBackgroundColor;
+        repaintComponent();
+    }
+
+    public final void setInactiveSelectionBackgroundColor(String inactiveSelectionBackgroundColor) {
+        if (inactiveSelectionBackgroundColor == null) {
+            throw new IllegalArgumentException("inactiveSelectionBackgroundColor is null.");
+        }
+
+        setInactiveSelectionBackgroundColor(GraphicsUtilities.decodeColor(inactiveSelectionBackgroundColor));
+    }
+
     public Insets getMargin() {
         return margin;
     }
@@ -1473,7 +1732,12 @@
 
                     textArea.setSelection(selectionStart, selectionLength);
 
-                    caretX = caret.x;
+                    if (caret != null) {
+                        caretX = caret.x;
+                    } else if (selection != null) {
+                        Rectangle bounds = selection.getBounds();
+                        caretX = bounds.x;
+                    }
 
                     consumed = true;
                 } else if (keyCode == Keyboard.KeyCode.RIGHT) {
@@ -1500,7 +1764,12 @@
 
                     textArea.setSelection(selectionStart, selectionLength);
 
-                    caretX = caret.x;
+                    if (caret != null) {
+                        caretX = caret.x;
+                    } else if (selection != null) {
+                        Rectangle bounds = selection.getBounds();
+                        caretX = bounds.x + bounds.width;
+                    }
 
                     consumed = true;
                 } else if (keyCode == Keyboard.KeyCode.UP) {
@@ -1606,17 +1875,30 @@
     @Override
     public void selectionChanged(TextArea textArea, int previousSelectionStart,
         int previousSelectionLength) {
-        if (documentView != null) {
-            if (textArea.getSelectionLength() == 0) {
-                // Repaint the previous caret bounds
+        // If the document view is valid, repaint the selection state; otherwise,
+        // the selection will be updated in layout()
+        if (documentView != null
+            && documentView.isValid()) {
+            // Repaint any previous caret bounds
+            if (caret != null) {
                 textArea.repaint(caret.x, caret.y, caret.width, caret.height);
+            }
+
+            // Repaint any previous selection bounds
+            if (selection != null) {
+                Rectangle bounds = selection.getBounds();
+                textArea.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
+            }
 
-                updateCaretBounds();
+            if (textArea.getSelectionLength() == 0) {
+                updateSelection();
                 showCaret(textArea.isFocused());
             } else {
-                // TODO Call updateCaretBounds() (rename to updateSelectionBounds())
-
+                updateSelection();
                 showCaret(false);
+
+                Rectangle bounds = selection.getBounds();
+                textArea.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
             }
         }
     }
@@ -1640,19 +1922,35 @@
         return nodeView;
     }
 
-    private void updateCaretBounds() {
+    private void updateSelection() {
         TextArea textArea = (TextArea)getComponent();
-
         int selectionStart = textArea.getSelectionStart();
-        Bounds startCharacterBounds = getCharacterBounds(selectionStart);
+        int selectionLength = textArea.getSelectionLength();
+
+        Bounds leadingSelectionBounds = getCharacterBounds(selectionStart);
 
-        if (startCharacterBounds != null) {
-            caret.x = startCharacterBounds.x;
-            caret.y = startCharacterBounds.y;
+        if (selectionLength == 0) {
+            caret = leadingSelectionBounds.toRectangle();
             caret.width = 1;
-            caret.height = startCharacterBounds.height;
+
+            selection = null;
         } else {
-            System.err.println("Selection bounds are null.");
+            caret = null;
+
+            Bounds trailingSelectionBounds = getCharacterBounds(selectionStart
+                + selectionLength - 1);
+            selection = new Area();
+
+            int firstRowIndex = getRowIndex(selectionStart);
+            int lastRowIndex = getRowIndex(selectionStart + selectionLength - 1);
+
+            if (firstRowIndex == lastRowIndex) {
+                selection.add(new Area(new Rectangle(leadingSelectionBounds.x, leadingSelectionBounds.y,
+                    trailingSelectionBounds.x + trailingSelectionBounds.width - leadingSelectionBounds.x,
+                    trailingSelectionBounds.y + trailingSelectionBounds.height - leadingSelectionBounds.y)));
+            } else {
+                // TODO Create two rectangles that cover the entire selection
+            }
         }
     }
 

Added: incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTextAreaSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTextAreaSkin.java?rev=829896&view=auto
==============================================================================
--- incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTextAreaSkin.java (added)
+++ incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTextAreaSkin.java Mon Oct 26 18:45:15 2009
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.pivot.wtk.skin.terra;
+
+import org.apache.pivot.wtk.Theme;
+import org.apache.pivot.wtk.skin.TextAreaSkin;
+
+/**
+ * Terra text area skin.
+ */
+public class TerraTextAreaSkin extends TextAreaSkin {
+    public TerraTextAreaSkin() {
+        setSelectionColor(4);
+        setSelectionBackgroundColor(19);
+        setInactiveSelectionColor(1);
+        setInactiveSelectionBackgroundColor(9);
+    }
+
+    public final void setColor(int color) {
+        TerraTheme theme = (TerraTheme)Theme.getTheme();
+        setColor(theme.getColor(color));
+    }
+
+    public final void setBackgroundColor(int backgroundColor) {
+        TerraTheme theme = (TerraTheme)Theme.getTheme();
+        setBackgroundColor(theme.getColor(backgroundColor));
+    }
+
+    public final void setSelectionColor(int backgroundColor) {
+        TerraTheme theme = (TerraTheme)Theme.getTheme();
+        setSelectionColor(theme.getColor(backgroundColor));
+    }
+
+    public final void setSelectionBackgroundColor(int backgroundColor) {
+        TerraTheme theme = (TerraTheme)Theme.getTheme();
+        setSelectionBackgroundColor(theme.getColor(backgroundColor));
+    }
+
+    public final void setInactiveSelectionColor(int backgroundColor) {
+        TerraTheme theme = (TerraTheme)Theme.getTheme();
+        setInactiveSelectionColor(theme.getColor(backgroundColor));
+    }
+
+    public final void setInactiveSelectionBackgroundColor(int backgroundColor) {
+        TerraTheme theme = (TerraTheme)Theme.getTheme();
+        setInactiveSelectionBackgroundColor(theme.getColor(backgroundColor));
+    }
+}

Modified: incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTheme.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTheme.java?rev=829896&r1=829895&r2=829896&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTheme.java (original)
+++ incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTheme.java Mon Oct 26 18:45:15 2009
@@ -70,6 +70,7 @@
 import org.apache.pivot.wtk.TablePane;
 import org.apache.pivot.wtk.TableView;
 import org.apache.pivot.wtk.TableViewHeader;
+import org.apache.pivot.wtk.TextArea;
 import org.apache.pivot.wtk.TextInput;
 import org.apache.pivot.wtk.Theme;
 import org.apache.pivot.wtk.Tooltip;
@@ -129,6 +130,7 @@
         componentSkinMap.put(TableViewHeader.class, TerraTableViewHeaderSkin.class);
         componentSkinMap.put(TableView.class, TerraTableViewSkin.class);
         componentSkinMap.put(TabPane.class, TerraTabPaneSkin.class);
+        componentSkinMap.put(TextArea.class, TerraTextAreaSkin.class);
         componentSkinMap.put(TextInput.class, TerraTextInputSkin.class);
         componentSkinMap.put(Tooltip.class, TerraTooltipSkin.class);
         componentSkinMap.put(TreeView.class, TerraTreeViewSkin.class);