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/03 21:34:00 UTC
svn commit: r992428 - in /pivot/trunk/wtk/src/org/apache/pivot/wtk:
FocusTraversalDirection.java MenuBar.java TextArea2.java
TextAreaContentListener2.java skin/TextAreaSkin2.java
Author: gbrown
Date: Fri Sep 3 19:33:59 2010
New Revision: 992428
URL: http://svn.apache.org/viewvc?rev=992428&view=rev
Log:
Continue building out new plain TextArea component.
Modified:
pivot/trunk/wtk/src/org/apache/pivot/wtk/FocusTraversalDirection.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/MenuBar.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/TextArea2.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/TextAreaContentListener2.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextAreaSkin2.java
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/FocusTraversalDirection.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/FocusTraversalDirection.java?rev=992428&r1=992427&r2=992428&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/FocusTraversalDirection.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/FocusTraversalDirection.java Fri Sep 3 19:33:59 2010
@@ -17,7 +17,7 @@
package org.apache.pivot.wtk;
/**
- * Enumeration representing a two-dimensional direction.
+ * Enumeration representing a focus traversal direction.
*/
public enum FocusTraversalDirection {
FORWARD,
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/MenuBar.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/MenuBar.java?rev=992428&r1=992427&r2=992428&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/MenuBar.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/MenuBar.java Fri Sep 3 19:33:59 2010
@@ -170,7 +170,19 @@ public class MenuBar extends Container {
* Item listener interface.
*/
public interface ItemListener {
+ /**
+ * Called when an item's menu has changed.
+ *
+ * @param item
+ * @param previousMenu
+ */
public void menuChanged(Item item, Menu previousMenu);
+
+ /**
+ * Called when an item's active state has changed.
+ *
+ * @param item
+ */
public void activeChanged(Item item);
}
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/TextArea2.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/TextArea2.java?rev=992428&r1=992427&r2=992428&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/TextArea2.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/TextArea2.java Fri Sep 3 19:33:59 2010
@@ -23,10 +23,13 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.URL;
+import java.util.Iterator;
import java.util.Locale;
import org.apache.pivot.collections.ArrayList;
+import org.apache.pivot.collections.Sequence;
import org.apache.pivot.json.JSON;
+import org.apache.pivot.util.ImmutableIterator;
import org.apache.pivot.util.ListenerList;
/**
@@ -34,6 +37,96 @@ import org.apache.pivot.util.ListenerLis
*/
public class TextArea2 extends Component {
/**
+ * Class representing a paragraph of text.
+ */
+ public final class Paragraph {
+ private StringBuilder characters;
+
+ private Paragraph() {
+ characters = new StringBuilder(INITIAL_PARAGRAPH_CAPACITY);
+ }
+
+ public CharSequence getCharacters() {
+ return characters;
+ }
+
+ public void insertText(String text, int index) {
+ characters.insert(index, text);
+
+ // TODO Fire event
+ // TODO Update selection state
+ }
+
+ public void removeText(int index, int count) {
+ characters.delete(index, index + count);
+
+ // TODO Fire event
+ // TODO Update selection state
+ }
+
+ public int getOffset() {
+ // TODO
+ return -1;
+ }
+
+ // TODO Add listener list accessor
+ }
+
+ /**
+ * Paragraph listener interface.
+ */
+ public interface ParagraphListener {
+ /**
+ * Paragraph listener interface adapter.
+ */
+ public static class Adapter implements ParagraphListener {
+ @Override
+ public void textInserted(Paragraph paragraph, int index, int count) {
+ }
+
+ @Override
+ public void textRemoved(Paragraph paragraph, int index, int count) {
+ }
+ }
+
+ /**
+ * Called when text has been inserted into a paragraph.
+ *
+ * @param paragraph
+ * The source of the event.
+ *
+ * @param index
+ * The index at which the text was inserted.
+ *
+ * @param count
+ * The number of characters that were inserted.
+ */
+ public void textInserted(Paragraph paragraph, int index, int count);
+
+ /**
+ * Called when characters have been removed from a paragraph.
+ *
+ * @param paragraph
+ * The source of the event.
+ *
+ * @param index
+ * The index from which the text was removed.
+ *
+ * @param count
+ * The number of characters that were removed.
+ */
+ public void textRemoved(Paragraph paragraph, int index, int count);
+ }
+
+ /**
+ * Enum representing a scroll direction.
+ */
+ public enum ScrollDirection {
+ UP,
+ DOWN
+ }
+
+ /**
* Text area skin interface. Text area skins are required to implement
* this.
*/
@@ -54,7 +147,7 @@ public class TextArea2 extends Component
* @param from
* @param direction
*/
- public int getNextInsertionPoint(int x, int from, FocusTraversalDirection direction);
+ public int getNextInsertionPoint(int x, int from, ScrollDirection direction);
/**
* Returns the row index of the character at a given index.
@@ -97,6 +190,51 @@ public class TextArea2 extends Component
public Object valueOf(String text);
}
+ public final class ParagraphSequence implements Sequence<Paragraph>, Iterable<Paragraph> {
+ public int add(Paragraph paragraph) {
+ // TODO
+ return -1;
+ }
+
+ public void insert(Paragraph paragraph, int index) {
+ // TODO
+ }
+
+ public Paragraph update(int index, Paragraph paragraph) {
+ // TODO
+ return null;
+ }
+
+ public int remove(Paragraph paragraph){
+ // TODO
+ return -1;
+ }
+
+ public Sequence<Paragraph> remove(int index, int count) {
+ // TODO
+ return null;
+ }
+
+ public Paragraph get(int index) {
+ // TODO
+ return null;
+ }
+
+ public int indexOf(Paragraph paragraph) {
+ // TODO
+ return -1;
+ }
+
+ public int getLength() {
+ // TODO
+ return -1;
+ }
+
+ public Iterator<Paragraph> iterator() {
+ return new ImmutableIterator<Paragraph>(paragraphs.iterator());
+ }
+ }
+
private static class TextAreaListenerList extends ListenerList<TextAreaListener2>
implements TextAreaListener2 {
@Override
@@ -117,16 +255,16 @@ public class TextArea2 extends Component
private static class TextAreaContentListenerList extends ListenerList<TextAreaContentListener2>
implements TextAreaContentListener2 {
@Override
- public void textInserted(TextArea2 textArea, int index, int count) {
+ public void paragraphInserted(TextArea2 textArea, int index) {
for (TextAreaContentListener2 listener : this) {
- listener.textInserted(textArea, index, count);
+ listener.paragraphInserted(textArea, index);
}
}
@Override
- public void textRemoved(TextArea2 textArea, int index, int count) {
+ public void paragraphsRemoved(TextArea2 textArea, int index, Sequence<TextArea2.Paragraph> removed) {
for (TextAreaContentListener2 listener : this) {
- listener.textRemoved(textArea, index, count);
+ listener.paragraphsRemoved(textArea, index, removed);
}
}
@@ -174,7 +312,9 @@ public class TextArea2 extends Component
}
}
- private ArrayList<StringBuilder> paragraphs = null;
+ private ArrayList<Paragraph> paragraphs = new ArrayList<Paragraph>();
+ private ParagraphSequence paragraphSequence = new ParagraphSequence();
+
private int characterCount = 0;
private int selectionStart = 0;
@@ -195,8 +335,6 @@ public class TextArea2 extends Component
private static final int INITIAL_PARAGRAPH_CAPACITY = 256;
public TextArea2() {
- setText("");
-
installThemeSkin(TextArea2.class);
}
@@ -241,36 +379,7 @@ public class TextArea2 extends Component
StringBuilder textBuilder = new StringBuilder(endIndex - beginIndex);
- // Determine the start of the range
- int paragraphIndex = 0;
- int characterOffset = 0;
-
- StringBuilder paragraph = paragraphs.get(paragraphIndex);
- int paragraphLength = paragraph.length();
-
- while (characterOffset + paragraphLength < beginIndex) {
- characterOffset += paragraphLength;
-
- paragraph = paragraphs.get(++paragraphIndex);
- paragraphLength = paragraph.length();
- }
-
- characterOffset = beginIndex - characterOffset;
-
- // Walk from begin index to end index copying characters into text builder
- for (int index = beginIndex; index < endIndex; index++) {
- textBuilder.append(paragraph.charAt(characterOffset++));
-
- if (characterOffset == paragraphLength) {
- if (index != endIndex - 1) {
- textBuilder.append('\n');
- }
-
- paragraph = paragraphs.get(++paragraphIndex);
- paragraphLength = paragraph.length();
- characterOffset = 0;
- }
- }
+ // TODO
return textBuilder.toString();
}
@@ -318,26 +427,12 @@ public class TextArea2 extends Component
}
// Construct the paragraph list
- paragraphs = new ArrayList<StringBuilder>();
+ paragraphs.clear();
characterCount = 0;
- StringBuilder paragraph = new StringBuilder(INITIAL_PARAGRAPH_CAPACITY);
-
- int c = textReader.read();
- while (c != -1) {
- if (c == '\n') {
- paragraphs.add(paragraph);
- paragraph = new StringBuilder(INITIAL_PARAGRAPH_CAPACITY);
- } else {
- paragraph.append(c);
- }
-
- characterCount++;
-
- c = textReader.read();
- }
+ // TODO
- paragraphs.add(paragraph);
+ // TODO set characterCount
// Update selection
int previousSelectionStart = selectionStart;
@@ -369,53 +464,7 @@ public class TextArea2 extends Component
}
if (text.length() > 0) {
- // Determine the insertion point
- int paragraphIndex, characterOffset;
- if (index == characterCount) {
- paragraphIndex = paragraphs.getLength() - 1;
- characterOffset = paragraphs.get(paragraphIndex).length();
- } else {
- paragraphIndex = 0;
- characterOffset = 0;
-
- int paragraphCount = paragraphs.getLength();
- while (paragraphIndex < paragraphCount) {
- StringBuilder paragraph = paragraphs.get(paragraphIndex);
- int paragraphLength = paragraph.length();
-
- if (characterOffset + paragraphLength > index) {
- break;
- } else {
- characterOffset += paragraphLength;
- paragraphIndex++;
- }
- }
-
- characterOffset = index - characterOffset;
- }
-
- // Insert the text
- StringBuilder textBuilder = new StringBuilder();
-
- for (int i = 0, n = text.length(); i < n; i++) {
- char c = text.charAt(i);
-
- if (c == '\n') {
- StringBuilder paragraph = paragraphs.get(paragraphIndex);
- paragraph.insert(characterOffset, textBuilder);
-
- characterOffset += textBuilder.length();
- textBuilder = new StringBuilder(paragraph.substring(characterOffset));
- paragraphs.insert(textBuilder, ++paragraphIndex);
- } else {
- textBuilder.append(c);
- }
- }
-
- StringBuilder paragraph = paragraphs.get(paragraphIndex);
- paragraph.insert(characterOffset, textBuilder);
-
- characterCount += text.length();
+ // TODO
// Update selection
int previousSelectionStart = selectionStart;
@@ -424,7 +473,6 @@ public class TextArea2 extends Component
selectionLength = 0;
// Fire change events
- textAreaContentListeners.textInserted(this, previousSelectionStart, text.length());
textAreaContentListeners.textChanged(this);
if (selectionStart != previousSelectionStart
@@ -436,10 +484,7 @@ public class TextArea2 extends Component
public void removeText(int index, int count) {
if (count > 0) {
- // TODO Identify begin paragraph index/offset
- // TODO Identify end paragraph index/offset
- // TODO Remove leading/trailing characters
- // TODO Removing any intervening paragraphs
+ // TODO
characterCount -= count;
@@ -450,7 +495,6 @@ public class TextArea2 extends Component
selectionLength = 0;
// Fire change events
- textAreaContentListeners.textRemoved(this, selectionStart, count);
textAreaContentListeners.textChanged(this);
if (selectionStart != previousSelectionStart
@@ -461,19 +505,31 @@ public class TextArea2 extends Component
}
/**
- * Returns a character sequence representing a paragraph's content.
+ * Returns the text area's paragraph sequence.
+ */
+ public ParagraphSequence getParagraphs() {
+ return paragraphSequence;
+ }
+
+ /**
+ * Returns the index of the paragraph containing a given character index.
*
* @param index
*/
- public CharSequence getParagraph(int index) {
- return paragraphs.get(index);
+ public int getParagraphAt(int index) {
+ // TODO Be sure to return paragraph corresponding to terminator character,
+ // including implicit final terminator
+ return -1;
}
/**
- * Returns the number of paragraphs in the text area.
+ * Returns the character at a given index.
+ *
+ * @param index
*/
- public int getParagraphCount() {
- return paragraphs.getLength();
+ public char getCharacterAt(int index) {
+ // TODO
+ return 0x00;
}
/**
@@ -812,27 +868,27 @@ public class TextArea2 extends Component
}
public int getInsertionPoint(int x, int y) {
- TextArea.Skin textAreaSkin = (TextArea.Skin)getSkin();
+ TextArea2.Skin textAreaSkin = (TextArea2.Skin)getSkin();
return textAreaSkin.getInsertionPoint(x, y);
}
- public int getNextInsertionPoint(int x, int from, FocusTraversalDirection direction) {
- TextArea.Skin textAreaSkin = (TextArea.Skin)getSkin();
+ public int getNextInsertionPoint(int x, int from, ScrollDirection direction) {
+ TextArea2.Skin textAreaSkin = (TextArea2.Skin)getSkin();
return textAreaSkin.getNextInsertionPoint(x, from, direction);
}
public int getRowIndex(int index) {
- TextArea.Skin textAreaSkin = (TextArea.Skin)getSkin();
+ TextArea2.Skin textAreaSkin = (TextArea2.Skin)getSkin();
return textAreaSkin.getRowIndex(index);
}
public int getRowCount() {
- TextArea.Skin textAreaSkin = (TextArea.Skin)getSkin();
+ TextArea2.Skin textAreaSkin = (TextArea2.Skin)getSkin();
return textAreaSkin.getRowCount();
}
public Bounds getCharacterBounds(int index) {
- TextArea.Skin textAreaSkin = (TextArea.Skin)getSkin();
+ TextArea2.Skin textAreaSkin = (TextArea2.Skin)getSkin();
return textAreaSkin.getCharacterBounds(index);
}
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/TextAreaContentListener2.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/TextAreaContentListener2.java?rev=992428&r1=992427&r2=992428&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/TextAreaContentListener2.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/TextAreaContentListener2.java Fri Sep 3 19:33:59 2010
@@ -16,6 +16,8 @@
*/
package org.apache.pivot.wtk;
+import org.apache.pivot.collections.Sequence;
+
/**
* Text area text listener interface.
*/
@@ -25,11 +27,11 @@ public interface TextAreaContentListener
*/
public static class Adapter implements TextAreaContentListener2 {
@Override
- public void textInserted(TextArea2 textArea, int index, int count) {
+ public void paragraphInserted(TextArea2 textArea, int index) {
}
@Override
- public void textRemoved(TextArea2 textArea, int index, int count) {
+ public void paragraphsRemoved(TextArea2 textArea, int index, Sequence<TextArea2.Paragraph> removed) {
}
@Override
@@ -38,32 +40,31 @@ public interface TextAreaContentListener
}
/**
- * Called when text has been inserted into a text area.
+ * Called when a paragraph has been inserted into a text area's paragraph
+ * sequence.
*
* @param textArea
* The source of the event.
*
* @param index
- * The index at which the text was inserted.
- *
- * @param count
- * The number of characters that were inserted.
+ * The index at which the paragraph was inserted.
*/
- public void textInserted(TextArea2 textArea, int index, int count);
+ public void paragraphInserted(TextArea2 textArea, int index);
/**
- * Called when characters have been removed from a text area.
+ * Called when paragraphs have been removed from a text area's paragraph
+ * sequence.
*
* @param textArea
* The source of the event.
*
* @param index
- * The index from which the text was removed.
+ * The starting index from which the paragraphs were removed.
*
- * @param count
- * The number of characters that were removed.
+ * @param removed
+ * The paragraphs that were removed.
*/
- public void textRemoved(TextArea2 textArea, int index, int count);
+ public void paragraphsRemoved(TextArea2 textArea, int index, Sequence<TextArea2.Paragraph> removed);
/**
* Called when a text area's text has changed.
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=992428&r1=992427&r2=992428&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 Fri Sep 3 19:33:59 2010
@@ -19,26 +19,194 @@ package org.apache.pivot.wtk.skin;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
+import java.awt.Rectangle;
import java.awt.Transparency;
+import java.awt.font.GlyphVector;
+import java.awt.geom.Area;
+import org.apache.pivot.collections.ArrayList;
import org.apache.pivot.collections.Dictionary;
+import org.apache.pivot.collections.Sequence;
+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.FocusTraversalDirection;
import org.apache.pivot.wtk.GraphicsUtilities;
import org.apache.pivot.wtk.Insets;
+import org.apache.pivot.wtk.Keyboard;
+import org.apache.pivot.wtk.Mouse;
+import org.apache.pivot.wtk.Platform;
import org.apache.pivot.wtk.TextArea2;
import org.apache.pivot.wtk.TextAreaListener2;
import org.apache.pivot.wtk.TextAreaContentListener2;
import org.apache.pivot.wtk.TextAreaSelectionListener2;
import org.apache.pivot.wtk.Theme;
+import org.apache.pivot.wtk.Visual;
/**
* Text area skin.
*/
public class TextAreaSkin2 extends ComponentSkin implements TextArea2.Skin,
TextAreaListener2, TextAreaContentListener2, TextAreaSelectionListener2 {
+ /**
+ * Paragraph view.
+ */
+ public class ParagraphView implements Visual, TextArea2.ParagraphListener {
+ private TextArea2.Paragraph paragraph;
+
+ 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>();
+
+ public ParagraphView(TextArea2.Paragraph paragraph) {
+ this.paragraph = paragraph;
+ }
+
+ public TextArea2.Paragraph getParagraph() {
+ return paragraph;
+ }
+
+ @Override
+ public int getWidth() {
+ validate();
+ return width;
+ }
+
+ @Override
+ public int getHeight() {
+ validate();
+ return height;
+ }
+
+ public int getBreakWidth() {
+ return breakWidth;
+ }
+
+ public void setBreakWidth(int breakWidth) {
+ int previousBreakWidth = this.breakWidth;
+ this.breakWidth = breakWidth;
+
+ if (previousBreakWidth > breakWidth) {
+ invalidate();
+ }
+ }
+
+ @Override
+ public int getBaseline() {
+ return -1;
+ }
+
+ @Override
+ public void paint(Graphics2D graphics) {
+ // Iterate over rows and paint each
+ // TODO Only paint visible glyphs
+ for (GlyphVector line : rows) {
+ // TODO
+ graphics.drawGlyphVector(line, 0, 0);
+ }
+ }
+
+ public void invalidate() {
+ valid = false;
+ }
+
+ public void validate() {
+ if (!valid) {
+ // TODO Re-layout glyphs and recalculate size
+ rows = new ArrayList<GlyphVector>();
+ width = 0;
+ height = 0;
+ }
+
+ valid = true;
+ }
+
+ @Override
+ public void textInserted(TextArea2.Paragraph paragraph, int index, int count) {
+ invalidate();
+ invalidateComponent();
+ }
+
+ @Override
+ public void textRemoved(TextArea2.Paragraph paragraph, int index, int count) {
+ invalidate();
+ invalidateComponent();
+ }
+ }
+
+ private class BlinkCaretCallback implements Runnable {
+ @Override
+ public void run() {
+ caretOn = !caretOn;
+
+ if (selection == null) {
+ TextArea2 textArea = (TextArea2)getComponent();
+ textArea.repaint(caret.x, caret.y, caret.width, caret.height, true);
+ }
+ }
+ }
+
+ private class ScrollSelectionCallback implements Runnable {
+ @Override
+ public void run() {
+ TextArea2 textArea = (TextArea2)getComponent();
+ int selectionStart = textArea.getSelectionStart();
+ int selectionLength = textArea.getSelectionLength();
+ int selectionEnd = selectionStart + selectionLength - 1;
+
+ switch (scrollDirection) {
+ case UP: {
+ // Get previous offset
+ int offset = getNextInsertionPoint(mouseX, selectionStart, scrollDirection);
+
+ if (offset != -1) {
+ textArea.setSelection(offset, selectionEnd - offset + 1);
+ scrollCharacterToVisible(offset + 1);
+ }
+
+ break;
+ }
+
+ case DOWN: {
+ // Get next offset
+ int offset = getNextInsertionPoint(mouseX, selectionEnd, scrollDirection);
+
+ if (offset != -1) {
+ // If the next character is a paragraph terminator, increment the selection
+ if (textArea.getCharacterAt(offset) == '\n') {
+ offset++;
+ }
+
+ textArea.setSelection(selectionStart, offset - selectionStart);
+ scrollCharacterToVisible(offset - 1);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ private int caretX = 0;
+ private Rectangle caret = new Rectangle();
+ private Area selection = null;
+
+ private boolean caretOn = false;
+
+ private int anchor = -1;
+ private TextArea2.ScrollDirection scrollDirection = null;
+ private int mouseX = -1;
+
+ private BlinkCaretCallback blinkCaretCallback = new BlinkCaretCallback();
+ private ApplicationContext.ScheduledCallback scheduledBlinkCaretCallback = null;
+
+ private ScrollSelectionCallback scrollSelectionCallback = new ScrollSelectionCallback();
+ private ApplicationContext.ScheduledCallback scheduledScrollSelectionCallback = null;
+
private Font font;
private Color color;
private Color backgroundColor;
@@ -47,11 +215,11 @@ 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 ArrayList<ParagraphView> paragraphViews = new ArrayList<ParagraphView>();
+
public TextAreaSkin2() {
Theme theme = Theme.getTheme();
font = theme.getFont();
@@ -68,35 +236,77 @@ public class TextAreaSkin2 extends Compo
public void install(Component component) {
super.install(component);
- // TODO
+ TextArea2 textArea = (TextArea2)component;
+ textArea.getTextAreaListeners().add(this);
+ textArea.getTextAreaContentListeners().add(this);
+ textArea.getTextAreaSelectionListeners().add(this);
}
@Override
public int getPreferredWidth(int height) {
- // TODO
- return 0;
+ int preferredWidth = 0;
+
+ for (ParagraphView paragraphView : paragraphViews) {
+ paragraphView.setBreakWidth(Integer.MAX_VALUE);
+ preferredWidth = Math.max(preferredWidth, paragraphView.getWidth());
+ }
+
+ preferredWidth += margin.left + margin.right;
+
+ return preferredWidth;
}
@Override
public int getPreferredHeight(int width) {
- // TODO
- return 0;
+ int preferredHeight = 0;
+
+ // Include margin in constraint
+ int breakWidth = (wrapText
+ && width != -1) ? Math.max(width - (margin.left + margin.right), 0) : Integer.MAX_VALUE;
+
+ for (ParagraphView paragraphView : paragraphViews) {
+ paragraphView.setBreakWidth(breakWidth);
+ preferredHeight += paragraphView.getHeight();
+ }
+
+ preferredHeight += margin.top + margin.bottom;
+
+ return preferredHeight;
}
@Override
public Dimensions getPreferredSize() {
- // TODO
- return new Dimensions(0, 0);
+ int preferredWidth = 0;
+ int preferredHeight = 0;
+
+ for (ParagraphView paragraphView : paragraphViews) {
+ paragraphView.setBreakWidth(Integer.MAX_VALUE);
+ preferredWidth = Math.max(preferredWidth, paragraphView.getWidth());
+ preferredHeight += paragraphView.getHeight();
+ }
+
+ preferredWidth += margin.left + margin.right;
+ preferredHeight += margin.top + margin.bottom;
+
+ return new Dimensions(preferredWidth, preferredHeight);
}
@Override
public void layout() {
- // TODO
+ int width = getWidth();
+
+ for (ParagraphView paragraphView : paragraphViews) {
+ paragraphView.setBreakWidth(width);
+
+ // TODO Set location
+ }
}
@Override
public void paint(Graphics2D graphics) {
- // TODO
+ // TODO Paint selection state
+ // TODO Walk paragraph view list and translate graphics/paint each view (only
+ // paint visible views)
}
@Override
@@ -110,7 +320,7 @@ public class TextAreaSkin2 extends Compo
return -1;
}
- public int getNextInsertionPoint(int x, int from, FocusTraversalDirection direction) {
+ public int getNextInsertionPoint(int x, int from, TextArea2.ScrollDirection direction) {
// TODO
return -1;
}
@@ -126,10 +336,20 @@ public class TextAreaSkin2 extends Compo
}
public Bounds getCharacterBounds(int index) {
- // TODO
+ // TODO Check for paragraph terminators including implicit final terminator
return null;
}
+ private void scrollCharacterToVisible(int offset) {
+ TextArea2 textArea = (TextArea2)getComponent();
+ Bounds characterBounds = getCharacterBounds(offset);
+
+ if (characterBounds != null) {
+ textArea.scrollAreaToVisible(characterBounds.x, characterBounds.y,
+ characterBounds.width, characterBounds.height);
+ }
+ }
+
public Font getFont() {
return font;
}
@@ -353,26 +573,93 @@ public class TextAreaSkin2 extends Compo
}
@Override
- public void maximumLengthChanged(TextArea2 textArea, int previousMaximumLength) {
+ public boolean mouseMove(Component component, int x, int y) {
+ boolean consumed = super.mouseMove(component, x, y);
+
// TODO
+
+ return consumed;
}
@Override
- public void editableChanged(TextArea2 textArea) {
+ public boolean mouseDown(Component component, Mouse.Button button, int x, int y) {
+ boolean consumed = super.mouseDown(component, button, x, y);
+
+ // TODO
+
+ return consumed;
+ }
+
+ @Override
+ public boolean mouseUp(Component component, Mouse.Button button, int x, int y) {
+ boolean consumed = super.mouseUp(component, button, x, y);
+
+ // TODO
+
+ return consumed;
+ }
+
+ @Override
+ public boolean keyTyped(Component component, char character) {
+ boolean consumed = super.keyTyped(component, character);
+
+ // TODO
+
+ return consumed;
+ }
+
+ @Override
+ public boolean keyPressed(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
+ boolean consumed = super.keyPressed(component, keyCode, keyLocation);
+
// TODO
+
+ return consumed;
+ }
+
+ @Override
+ public void enabledChanged(Component component) {
+ super.enabledChanged(component);
+ repaintComponent();
+ }
+
+ @Override
+ public void focusedChanged(Component component, Component obverseComponent) {
+ super.focusedChanged(component, obverseComponent);
+
+ TextArea2 textArea = (TextArea2)getComponent();
+ if (textArea.isFocused()
+ && textArea.getSelectionLength() == 0) {
+ scrollCharacterToVisible(textArea.getSelectionStart());
+ showCaret(true);
+ } else {
+ showCaret(false);
+ }
+
+ repaintComponent();
}
@Override
- public void textInserted(TextArea2 textArea, int index, int count) {
+ public void maximumLengthChanged(TextArea2 textArea, int previousMaximumLength) {
// TODO
}
@Override
- public void textRemoved(TextArea2 textArea, int index, int count) {
+ public void editableChanged(TextArea2 textArea) {
// TODO
}
@Override
+ public void paragraphInserted(TextArea2 textArea, int index) {
+ // TODO Create paragraph view and add as paragraph listener
+ }
+
+ @Override
+ public void paragraphsRemoved(TextArea2 textArea, int index, Sequence<TextArea2.Paragraph> removed) {
+ // TODO Remove paragraph views and remove as paragraph listeners
+ }
+
+ @Override
public void textChanged(TextArea2 textArea) {
// TODO
}
@@ -382,4 +669,78 @@ public class TextAreaSkin2 extends Compo
int previousSelectionLength) {
// TODO
}
+
+ private void updateSelection() {
+ TextArea2 textArea = (TextArea2)getComponent();
+
+ if (textArea.getCharacterCount() > 0) {
+ // Update the caret
+ int selectionStart = textArea.getSelectionStart();
+
+ Bounds leadingSelectionBounds = getCharacterBounds(selectionStart);
+ caret = leadingSelectionBounds.toRectangle();
+ caret.width = 1;
+
+ // Update the selection
+ int selectionLength = textArea.getSelectionLength();
+
+ if (selectionLength > 0) {
+ int selectionEnd = selectionStart + selectionLength - 1;
+ Bounds trailingSelectionBounds = getCharacterBounds(selectionEnd);
+ selection = new Area();
+
+ int firstRowIndex = getRowIndex(selectionStart);
+ int lastRowIndex = getRowIndex(selectionEnd);
+
+ 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 {
+ int width = getWidth();
+
+ selection.add(new Area(new Rectangle(leadingSelectionBounds.x,
+ leadingSelectionBounds.y,
+ width - margin.right - leadingSelectionBounds.x,
+ leadingSelectionBounds.height)));
+
+ if (lastRowIndex - firstRowIndex > 0) {
+ selection.add(new Area(new Rectangle(margin.left,
+ leadingSelectionBounds.y + leadingSelectionBounds.height,
+ width - (margin.left + margin.right),
+ trailingSelectionBounds.y - (leadingSelectionBounds.y
+ + leadingSelectionBounds.height))));
+ }
+
+ selection.add(new Area(new Rectangle(margin.left, trailingSelectionBounds.y,
+ trailingSelectionBounds.x + trailingSelectionBounds.width - margin.left,
+ trailingSelectionBounds.height)));
+ }
+ } else {
+ selection = null;
+ }
+ } else {
+ // Clear the caret and the selection
+ caret = new Rectangle();
+ selection = null;
+ }
+ }
+
+ private void showCaret(boolean show) {
+ if (scheduledBlinkCaretCallback != null) {
+ scheduledBlinkCaretCallback.cancel();
+ }
+
+ if (show) {
+ caretOn = true;
+ scheduledBlinkCaretCallback =
+ ApplicationContext.scheduleRecurringCallback(blinkCaretCallback,
+ Platform.getCursorBlinkRate());
+
+ // Run the callback once now to show the cursor immediately
+ blinkCaretCallback.run();
+ } else {
+ scheduledBlinkCaretCallback = null;
+ }
+ }
}