/*
* Copyright © 2013 Nokia Corporation. All rights reserved. Nokia and Nokia
* Connecting People are registered trademarks of Nokia Corporation. Oracle and
* Java are trademarks or registered trademarks of Oracle and/or its affiliates.
* Other product and company names mentioned herein may be trademarks or trade
* names of their respective owners. See LICENSE.TXT for license information.
*/
package com.nokia.example;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import com.nokia.mid.ui.CanvasGraphicsItem;
import com.nokia.mid.ui.TextEditor;
import com.nokia.mid.ui.TextEditorListener;
/**
* This class implements CanvasTextBox control with label, text editing area
* decorations and a keyboard indicator.
*
* CanvasTextBox is based on CanvasGraphicsItem, on which label and text editor
* borders are drawn. On Symbian platform, the keyboard indicator will be
* relocated from its default position. Also a very simple scrollbar is drawn to
* indicate position in case CanvasTextBox is constructed as multiline.
*
* CanvasTextBox can have normal, focused or dimmed (disabled) state. State is
* encapsulated in TextBoxState objects (class TextBoxState is implemented in
* this file).
*/
public class CanvasTextBox extends CanvasGraphicsItem implements
TextEditorListener {
private Font labelFont = null;
private String label;
private TextEditor textEditor;
private TextBoxState normalState;
private TextBoxState focusedState;
private TextBoxState dimmedState;
private TextBoxState currentState;
private boolean enabled = true;
private boolean focused = false;
private TextEditorListener listener;
private int controlWidth = 200;
private int textLimit;
private boolean multiline;
private Scrollbar scrollbar;
private Controls controls;
// Enable indicator on JRT version 2.1 for symbian devices by uncommenting the following code.
// JRT versions 2.2 and newer already contain the indicator in the virtual keyboard.
// private boolean showIndicator;
// Default control's margin
private final int margin = 3;
// Margin applied around text editor and its border
private final int textEditorBorderMargin = 9;
// Margin applied on text editor it self
private final int textEditorMargin = 12;
private final int editorYPadding = 10;
public CanvasTextBox(Canvas parent, String label, int type, int textLimit,
boolean multiline) {
super(1, 1);
this.setParent(parent);
this.label = label;
this.textLimit = textLimit;
this.multiline = multiline;
this.labelFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN,
Font.SIZE_LARGE);
this.initializeTextEditor(parent, type);
this.createStates();
this.setAutomaticSize();
}
public CanvasTextBox(Canvas parent, String label, int type, int textLimit) {
// Construct single line text box with default width
this(parent, label, type, textLimit, false);
}
public String getText() {
return textEditor.getContent();
}
public boolean isEmpty() {
return textEditor.size() == 0;
}
public void clearChar() {
int caret = textEditor.getCaretPosition() - 1;
if (caret >= 0) {
textEditor.delete(textEditor.getCaretPosition() - 1, 1);
repaint();
}
}
public void setVisible(boolean visible) {
super.setVisible(visible);
textEditor.setVisible(visible);
}
/**
* Sets the control position.
*
* Repositions are also nested TextEditor and indicator (if supported).
*/
public void setPosition(int x, int y) {
// Set position of the underlying CanvasGraphicsItem
super.setPosition(x, y);
// Calculate and sed position of TextEditor
int editorX = x + (this.controlWidth - this.editorWidth()) / 2;
int editorY = y + labelFont.getHeight() + 2 * this.margin
+ (this.currentState.height - this.textEditor.getHeight()) / 2
+ ((BlogWriter.isAshaPlatform()) ? editorYPadding : 0);
this.textEditor.setPosition(editorX, editorY);
// Where supported, re-position also keyboard indicator
/*
//Enable indicator on JRT version 2.1 for symbian devices by uncommenting the following code.
//JRT versions 2.2 and newer already contain the indicator in the virtual keyboard.
if (this.textEditor instanceof com.nokia.mid.ui.S60TextEditor) {
com.nokia.mid.ui.S60TextEditor s60Editor = (com.nokia.mid.ui.S60TextEditor) this.textEditor;
s60Editor.setIndicatorLocation(editorX, y + labelFont.getHeight()
+ this.currentState.getHeight() + 3 * this.margin);
s60Editor.setIndicatorVisibility(this.focused && this.showIndicator);
}*/
}
/**
* Sets size of the CanvasTextBox.
*
* This method re-layouts nested TextEditor and indicator (if supported).
*/
public void setSize(int w, int h) {
// Set size of underlying CanvasGraphicsItem
super.setSize(w, h);
// Calculate the size of TextEditor.
this.controlWidth = w;
if(BlogWriter.isAshaPlatform())
h += 2 * editorYPadding - 4;
int editorHeight = (h - labelFont.getHeight() - 3 * this.margin - 2 * this.textEditorBorderMargin) + (BlogWriter.isAshaPlatform() ? margin : 0);
this.textEditor.setSize(this.controlWidth - 2 * this.textEditorMargin
- (this.scrollbar != null ? Scrollbar.width : 0), editorHeight);
// States need to be re-created to reflect change in editor's size
this.createStates();
// This call updates indicator position
this.setPosition(this.getPositionX(), this.getPositionY());
}
public void setTextEditorListener(TextEditorListener listener) {
this.listener = listener;
}
/**
* Enables or disables control.
*/
public void setEnabled(boolean enabled) {
if (this.enabled == enabled) {
return;
}
this.enabled = enabled;
this.updateState();
this.repaint();
}
public boolean isEnabled() {
return this.enabled;
}
/**
* Sets focus of the CanvasTextBox. Focus is forwarded to TextEditor, and
* when control loses focus, it hides indicator (where it is supported).
*/
public void setFocused(boolean focused) {
if (this.focused == focused) {
//return;
}
this.focused = focused;
this.updateState();
if (this.textEditor.hasFocus() != this.focused) {
this.textEditor.setFocus(this.focused);
}
/*
//Enable indicator on JRT version 2.1 for symbian devices by uncommenting the following code.
//JRT versions 2.2 and newer already contain the indicator in the virtual keyboard.
if (this.textEditor instanceof com.nokia.mid.ui.S60TextEditor) {
com.nokia.mid.ui.S60TextEditor s60Editor = (com.nokia.mid.ui.S60TextEditor) this.textEditor;
s60Editor.setIndicatorVisibility(this.focused && this.showIndicator);
}*/
this.repaint();
}
public boolean isFocused() {
return this.focused;
}
/**
* This is very basic pointer event handling. It expects pointer pressed
* events.
*
* CanvasGraphicsItem does not receive any pointer events, they are
* delivered to parent Canvas, so this method needs to be called from
* Canvas.pointerPressed() override.
*/
public void handlePointerPressed(int x, int y) {
if (this.isVisible() && this.enabled) {
if (this.hitTest(x, y)) {
this.setFocused(true);
if (this.scrollbar != null) {
this.scrollbar.handlePointerPressed(x, y);
this.repaint();
}
if (this.controls != null) {
this.controls.handlePointerPressed(x - this.getPositionX(), y - this.getPositionY());
this.repaint();
}
} else {
this.setFocused(false);
}
}
}
/**
* This is very basic pointer event handling for pointer released events.
*/
public void handlePointerReleased(int x, int y) {
if (this.isVisible() && this.controls != null) {
this.controls.handlePointerReleased(x - this.getPositionX(), y - this.getPositionY());
this.repaint();
}
}
/**
* Checks whether given point belongs to the control. Coordinates are
* relative to parent Canvas.
*/
public boolean hitTest(int x, int y) {
return x >= this.getPositionX()
&& x < (this.getPositionX() + this.getWidth())
&& y >= this.getPositionY()
&& y < (this.getPositionY() + this.getHeight());
}
/**
* Before exiting MIDlet, it is necessary to set parents of both TextEditor
* and the base CanvasGraphics item to null.
*/
public void dispose() {
this.textEditor.setParent(null);
this.setParent(null);
}
/**
* Paints the label, currentState and scrollbar.
*/
public void paint(Graphics gfx) {
if (BlogWriter.isAshaPlatform()) {
gfx.setColor(0x8D8C8C);
gfx.fillRect(0, 0, getWidth(), getHeight());
}
gfx.setColor(this.currentState.labelColor);
gfx.drawString(this.label, this.margin, this.margin, Graphics.TOP | Graphics.LEFT);
int textEditorY = this.labelFont.getHeight() + 2 * this.margin;
this.currentState.paint(gfx,(this.controlWidth - this.currentState.width) / 2, textEditorY);
if (this.scrollbar != null) {
this.scrollbar.paint(gfx, this.textEditor.getWidth()
+ this.textEditorMargin, textEditorY
+ this.textEditorBorderMargin);
}
if (this.controls != null && this.isFocused()) {
this.controls.paint(gfx, (this.controlWidth - this.currentState.width) / 2 + this.currentState.width, textEditorY-1);
}
}
/**
* Handles some of the TextEditor events to support scrollbar and forwards
* events to external listener (if there is any).
*/
public void inputAction(TextEditor source, int type) {
if (source != this.textEditor) {
return;
}
if ((type & (TextEditorListener.ACTION_SCROLLBAR_CHANGED | TextEditorListener.ACTION_CARET_MOVE)) != 0) {
if (this.scrollbar != null) {
this.repaint();
}
}
if (this.listener != null) {
this.listener.inputAction(source, type);
}
}
/**
* Creates TextEditor instance and sets its properties.
*/
private void initializeTextEditor(Canvas parent, int type) {
this.textEditor = TextEditor.createTextEditor("", this.textLimit, type,
this.controlWidth - 2 * this.textEditorMargin,
(this.multiline ? 2 : 1) * (labelFont.getHeight()));
this.textEditor.setParent(parent);
if (this.multiline) {
this.textEditor.setMultiline(true);
/*
//Enable indicator on JRT version 2.1 for symbian devices by uncommenting the following code.
//JRT versions 2.2 and newer already contain the indicator in the virtual keyboard.
if (this.textEditor instanceof com.nokia.mid.ui.S60TextEditor) {
this.textEditor.setSize(this.textEditor.getWidth() - Scrollbar.width, this.textEditor.getHeight());
//this.scrollbar = new Scrollbar(this.textEditor, 0xaaaaaa, 0x101010);
}*/
if(BlogWriter.isS60Platform())
this.scrollbar = new Scrollbar(this.textEditor, 0xaaaaaa, 0x101010);
}
if(!BlogWriter.isS60Platform())
{
this.controls = new Controls(this.textEditor, 0x101010, 0xaaaaaa, 0xffffff);
}
this.textEditor.setTextEditorListener(this);
this.setZPosition(1);
this.textEditor.setZPosition(2);
}
/**
* Sets CanvasTextBox initial size.
*/
private void setAutomaticSize() {
// Calculate height so all part of the CanvasTextBox fit
int height = labelFont.getHeight() + this.normalState.getHeight() + 3
* this.margin;
/*
//Enable indicator on JRT version 2.1 for symbian devices by uncommenting the following code.
//JRT versions 2.2 and newer already contain the indicator in the virtual keyboard.
if (this.textEditor instanceof com.nokia.mid.ui.S60TextEditor) {
com.nokia.mid.ui.S60TextEditor s60Editor = (com.nokia.mid.ui.S60TextEditor) this.textEditor;
height += s60Editor.getIndicatorSize()[1] + this.margin;
}*/
super.setSize(this.controlWidth + 2 * this.margin, height);
}
/**
* Updates currentState based on state flags.
*/
private void updateState() {
if (this.enabled) {
if (this.focused) {
this.currentState = this.focusedState;
} else {
this.currentState = this.normalState;
}
} else {
this.currentState = this.dimmedState;
}
this.textEditor.setForegroundColor(this.currentState.textColor);
}
/**
* Creates CanvasTextBox states.
*/
private void createStates() {
int width = this.editorWidth() + 2 * this.textEditorBorderMargin;
int height;
if (BlogWriter.isAshaPlatform()) {
height = this.textEditor.getHeight() - 2;// + 2 * this.textEditorBorderMargin;
} else {
height = this.textEditor.getHeight() + 2 * this.textEditorBorderMargin;
}
this.normalState = new TextBoxState(width, height, 0x000000, 0xff000000,
0xe0e0e0);
this.focusedState = new TextBoxState(width, height, 0x000000, 0xff000000,
0xffffff);
this.dimmedState = new TextBoxState(width, height, 0xc5c5c5, 0xff313131,
0xa0a0a0);
this.updateState();
}
/**
* Calculates space needed for TextEditor. Scrollbar width is taken into
* account.
*/
private int editorWidth() {
return this.textEditor.getWidth()
+ (this.scrollbar != null ? Scrollbar.width : 0);
}
/**
* Encapsulates CanvasTextBox visual appearance.
*
* TextBoxState allows to specify label, text and background colors.
*
* When the size of the owning CanvasTextEditor changes, TextBoxStates need
* to be recreated since their width and height can only be set in the
* constructor.
*/
class TextBoxState {
public int backgroundColor;
public int labelColor;
public int textColor;
private int width;
private int height;
private final int cornersDiameter = 10;
private final int borderColor = 0x000000;
public TextBoxState(int width, int height, int labelColor,
int textColor, int backgroundColor) {
this.width = width;
this.height = height;
this.labelColor = labelColor;
this.textColor = textColor;
this.backgroundColor = backgroundColor;
}
public int getHeight() {
return this.height;
}
public int getWidth() {
return this.width;
}
/**
* Draws background and border. This method should be called from
* CanvasTextBox paint method.
*/
public void paint(Graphics gfx, int x, int y) {
gfx.setColor(this.backgroundColor);
gfx.fillRoundRect(x, y, this.width, this.height,
this.cornersDiameter, this.cornersDiameter);
gfx.setColor(this.borderColor);
gfx.drawRoundRect(x, y, this.width, this.height,
this.cornersDiameter, this.cornersDiameter);
}
}
}