diff --git a/apps/tests/ui/text-view/text-view-tests-native.android.ts b/apps/tests/ui/text-view/text-view-tests-native.android.ts index 89a503124..8f144aa56 100644 --- a/apps/tests/ui/text-view/text-view-tests-native.android.ts +++ b/apps/tests/ui/text-view/text-view-tests-native.android.ts @@ -16,6 +16,10 @@ export function getNativeEditable(textView: textViewModule.TextView): boolean { } } +export function getNativeHint(textView: textViewModule.TextView): string { + return textView.android.getHint(); +} + export function getNativeFontSize(textView: textViewModule.TextView): number { var density = utilsModule.layout.getDisplayDensity(); return textView.android.getTextSize() / density; diff --git a/apps/tests/ui/text-view/text-view-tests-native.d.ts b/apps/tests/ui/text-view/text-view-tests-native.d.ts index 151a5da26..6446745c2 100644 --- a/apps/tests/ui/text-view/text-view-tests-native.d.ts +++ b/apps/tests/ui/text-view/text-view-tests-native.d.ts @@ -3,6 +3,7 @@ import textViewModule = require("ui/text-view"); import colorModule = require("color"); export declare function getNativeText(textView: textViewModule.TextView): string; +export declare function getNativeHint(textView: textViewModule.TextView): string; export declare function getNativeEditable(textView: textViewModule.TextView): boolean; export declare function getNativeFontSize(textView: textViewModule.TextView): number; export declare function getNativeColor(textView: textViewModule.TextView): colorModule.Color; diff --git a/apps/tests/ui/text-view/text-view-tests-native.ios.ts b/apps/tests/ui/text-view/text-view-tests-native.ios.ts index 9afab1063..8393ddf81 100644 --- a/apps/tests/ui/text-view/text-view-tests-native.ios.ts +++ b/apps/tests/ui/text-view/text-view-tests-native.ios.ts @@ -7,6 +7,15 @@ export function getNativeText(textView: textViewModule.TextView): string { return textView.ios.text; } +export function getNativeHint(textView: textViewModule.TextView): string { + // There is no native hint so we use a hack and sett 22% opaque text. + if ((textView.ios).isShowingHint) { + return textView.ios.text; + } + + return ""; +} + export function getNativeEditable(textView: textViewModule.TextView): boolean { return textView.ios.editable; } diff --git a/apps/tests/ui/text-view/text-view-tests.ts b/apps/tests/ui/text-view/text-view-tests.ts index 417d60c3a..6ee4b5957 100644 --- a/apps/tests/ui/text-view/text-view-tests.ts +++ b/apps/tests/ui/text-view/text-view-tests.ts @@ -135,6 +135,130 @@ export var testTextIsUpdatedWhenUserTypes = function () { }); } +export var testSetHint = function () { + helper.buildUIAndRunTest(_createTextViewFunc(), function (views: Array) { + var textView = views[0]; + textView.text = ""; + + // + // ### Setting the hint of a TextView + // ``` JavaScript + textView.hint = "type your username here"; + // ``` + // + + var expectedValue = "type your username here"; + var actualValue = textViewTestsNative.getNativeHint(textView); + TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + }); +} + +export var testBindHintDirectlyToModel = function () { + helper.buildUIAndRunTest(_createTextViewFunc(), function (views: Array) { + var textView = views[0]; + textView.text = ""; + + // + // ### Binding hint property directly to model + // ``` JavaScript + var model = new observable.Observable(); + model.set("hint", "type your username here"); + var options: bindable.BindingOptions = { + sourceProperty: "hint", + targetProperty: "hint" + } + textView.bind(options, model); + //// TextView.hint is now "type your username here" + // + TKUnit.assert(textView.hint === "type your username here", "Actual: " + textView.hint + "; Expected: " + "type your username here"); + TKUnit.assert(textViewTestsNative.getNativeHint(textView) === "type your username here", "Actual: " + textViewTestsNative.getNativeHint(textView) + "; Expected: " + "type your username here"); + // + model.set("hint", "type your password here"); + //// TextView.hint is now "type your password here" + // + TKUnit.assert(textView.hint === "type your password here", "Actual: " + textView.hint + "; Expected: " + "type your password here"); + TKUnit.assert(textViewTestsNative.getNativeHint(textView) === "type your password here", "Actual: " + textViewTestsNative.getNativeHint(textView) + "; Expected: " + "type your password here"); + // + // ``` + // + }); +} + +export var testBindHintToBindingConext = function () { + helper.buildUIAndRunTest(_createTextViewFunc(), function (views: Array) { + var textView = views[0]; + textView.text = ""; + var page = views[1]; + + var model = new observable.Observable(); + model.set("hint", "type your username here"); + page.bindingContext = model; + + var options: bindable.BindingOptions = { + sourceProperty: "hint", + targetProperty: "hint" + } + + textView.bind(options); + TKUnit.assert(textView.hint === "type your username here", "Actual: " + textView.hint + "; Expected: " + "type your username here"); + TKUnit.assert(textViewTestsNative.getNativeHint(textView) === "type your username here", "Actual: " + textViewTestsNative.getNativeHint(textView) + "; Expected: " + "type your username here"); + + model.set("hint", "type your password here"); + TKUnit.assert(textView.hint === "type your password here", "Actual: " + textView.hint + "; Expected: " + "type your password here"); + TKUnit.assert(textViewTestsNative.getNativeHint(textView) === "type your password here", "Actual: " + textViewTestsNative.getNativeHint(textView) + "; Expected: " + "type your password here"); + }); +} + +export var testHintPlusTextiOS = function () { + helper.buildUIAndRunTest(_createTextViewFunc(), function (views: Array) { + var textView = views[0]; + if (!textView.ios) { + return; + } + + var expectedValue; + var actualValue; + + textView.hint = "hint"; + textView.text = "text"; + + expectedValue = "text"; + actualValue = textViewTestsNative.getNativeText(textView); + TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + + textView.text = ""; + expectedValue = "hint"; + actualValue = textViewTestsNative.getNativeText(textView); + TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + }); +} + +export var testHintColoriOS = function () { + helper.buildUIAndRunTest(_createTextViewFunc(), function (views: Array) { + var textView = views[0]; + if (!textView.ios) { + return; + } + + textView.text = ""; + textView.color = new colorModule.Color("red"); + textView.hint = "hint"; + + var expectedValue; + var actualValue; + + expectedValue = "#38.1999948ff0000"; // 22% red + actualValue = textViewTestsNative.getNativeColor(textView).hex; + TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + + textView.text = "text"; + + expectedValue = "#ffff0000"; // red + actualValue = textViewTestsNative.getNativeColor(textView).hex; + TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + }); +} + export var testSetEditable = function () { helper.buildUIAndRunTest(_createTextViewFunc(), function (views: Array) { var textView = views[0]; @@ -342,4 +466,4 @@ export var testMemoryLeak = function () { helper.buildUIWithWeakRefAndInteract(_createTextViewFunc, function (textView) { textViewTestsNative.typeTextNatively(textView, "Hello, world!"); }); -} +} \ No newline at end of file diff --git a/ui/editable-text-base/editable-text-base-common.ts b/ui/editable-text-base/editable-text-base-common.ts index c39332656..3d31c9b29 100644 --- a/ui/editable-text-base/editable-text-base-common.ts +++ b/ui/editable-text-base/editable-text-base-common.ts @@ -40,6 +40,12 @@ var autocorrectProperty = new dependencyObservable.Property( new proxy.PropertyMetadata(undefined, dependencyObservable.PropertyMetadataSettings.None) ); +export var hintProperty = new dependencyObservable.Property( + "hint", + "EditableTextBase", + new proxy.PropertyMetadata("") + ); + function onKeyboardTypePropertyChanged(data: dependencyObservable.PropertyChangeData) { var editableTextBase = data.object; editableTextBase._onKeyboardTypePropertyChanged(data); @@ -75,6 +81,13 @@ function onAutocorrectPropertyChanged(data: dependencyObservable.PropertyChangeD (autocorrectProperty.metadata).onSetNativeValue = onAutocorrectPropertyChanged; +function onHintPropertyChanged(data: dependencyObservable.PropertyChangeData) { + var editableTextBase = data.object; + editableTextBase._onHintPropertyChanged(data); +} + +(hintProperty.metadata).onSetNativeValue = onHintPropertyChanged; + export class EditableTextBase extends textBase.TextBase implements definition.EditableTextBase { public static keyboardTypeProperty = keyboardTypeProperty; @@ -89,6 +102,8 @@ export class EditableTextBase extends textBase.TextBase implements definition.Ed public static autocorrectProperty = autocorrectProperty; + public static hintProperty = hintProperty; + constructor(options?: definition.Options) { super(options); } @@ -141,6 +156,13 @@ export class EditableTextBase extends textBase.TextBase implements definition.Ed this._setValue(EditableTextBase.autocorrectProperty, value); } + get hint(): string { + return this._getValue(EditableTextBase.hintProperty); + } + set hint(value: string) { + this._setValue(EditableTextBase.hintProperty, value); + } + public dismissSoftInput() { // } @@ -165,4 +187,8 @@ export class EditableTextBase extends textBase.TextBase implements definition.Ed public _onAutocorrectPropertyChanged(data: dependencyObservable.PropertyChangeData) { // } + + public _onHintPropertyChanged(data: dependencyObservable.PropertyChangeData) { + // + } } \ No newline at end of file diff --git a/ui/editable-text-base/editable-text-base.android.ts b/ui/editable-text-base/editable-text-base.android.ts index 12aad1818..40c45c298 100644 --- a/ui/editable-text-base/editable-text-base.android.ts +++ b/ui/editable-text-base/editable-text-base.android.ts @@ -253,4 +253,13 @@ export class EditableTextBase extends common.EditableTextBase { editableTextBase.android.setInputType(inputType); } + + public _onHintPropertyChanged(data: dependencyObservable.PropertyChangeData) { + var editableTextBase = data.object; + if (!editableTextBase.android) { + return; + } + + editableTextBase.android.setHint(data.newValue); + } } \ No newline at end of file diff --git a/ui/editable-text-base/editable-text-base.d.ts b/ui/editable-text-base/editable-text-base.d.ts index 734acc8fd..b79da4e2e 100644 --- a/ui/editable-text-base/editable-text-base.d.ts +++ b/ui/editable-text-base/editable-text-base.d.ts @@ -46,6 +46,11 @@ */ autocorrect: boolean; + /** + * Gets or sets the placeholder text. + */ + hint: string; + /** * Hides the soft input method, ususally a soft keyboard. */ @@ -81,6 +86,11 @@ */ autocapitalizationType?: string; + /** + * Gets or sets the placeholder text. + */ + hint?: string; + /** * Enables or disables autocorrection. */ diff --git a/ui/styling/stylers.ios.ts b/ui/styling/stylers.ios.ts index 553024af2..aed8c699b 100644 --- a/ui/styling/stylers.ios.ts +++ b/ui/styling/stylers.ios.ts @@ -409,21 +409,36 @@ export class TextViewStyler implements definition.stylers.Styler { private static setColorProperty(view: view.View, newValue: any) { var textView: UITextView = view._nativeView; if (textView) { - textView.textColor = newValue; + TextViewStyler._setTextViewColor(textView, newValue); } } private static resetColorProperty(view: view.View, nativeValue: any) { var textView: UITextView = view._nativeView; if (textView) { - textView.textColor = nativeValue; + TextViewStyler._setTextViewColor(textView, nativeValue); + } + } + + private static _setTextViewColor(textView: UITextView, newValue: any) { + var color: UIColor = newValue; + if ((textView).isShowingHint && color) { + textView.textColor = (color).colorWithAlphaComponent(0.22); + } + else { + textView.textColor = color; } } private static getNativeColorValue(view: view.View): any { var textView: UITextView = view._nativeView; if (textView) { - return textView.textColor; + if ((textView).isShowingHint && textView.textColor) { + return textView.textColor.colorWithAlphaComponent(1); + } + else { + return textView.textColor; + } } } diff --git a/ui/text-base/text-base.d.ts b/ui/text-base/text-base.d.ts index 9dadae4dd..6df8155d5 100644 --- a/ui/text-base/text-base.d.ts +++ b/ui/text-base/text-base.d.ts @@ -39,6 +39,8 @@ * Gets or sets a formatted string. */ formattedText: formattedString.FormattedString; + + _onTextPropertyChanged(data: dependencyObservable.PropertyChangeData); } /** diff --git a/ui/text-field/text-field-common.ts b/ui/text-field/text-field-common.ts index 0c6f5c0de..d71702ffd 100644 --- a/ui/text-field/text-field-common.ts +++ b/ui/text-field/text-field-common.ts @@ -4,12 +4,6 @@ import proxy = require("ui/core/proxy"); import textBase = require("ui/text-base"); import editableTextBase = require("ui/editable-text-base"); -export var hintProperty = new dependencyObservable.Property( - "hint", - "TextField", - new proxy.PropertyMetadata("") - ); - export var secureProperty = new dependencyObservable.Property( "secure", "TextField", @@ -25,13 +19,6 @@ export class TextField extends editableTextBase.EditableTextBase implements defi super(options); } - get hint(): string { - return this._getValue(hintProperty); - } - set hint(value: string) { - this._setValue(hintProperty, value); - } - get secure(): boolean { return this._getValue(secureProperty); } diff --git a/ui/text-field/text-field.android.ts b/ui/text-field/text-field.android.ts index 926443421..e2e0ddc0d 100644 --- a/ui/text-field/text-field.android.ts +++ b/ui/text-field/text-field.android.ts @@ -2,18 +2,6 @@ import dependencyObservable = require("ui/core/dependency-observable"); import proxy = require("ui/core/proxy"); -function onHintPropertyChanged(data: dependencyObservable.PropertyChangeData) { - var textField = data.object; - if (!textField.android) { - return; - } - - textField.android.setHint(data.newValue); -} - -// register the setNativeValue callbacks -(common.hintProperty.metadata).onSetNativeValue = onHintPropertyChanged; - function onSecurePropertyChanged(data: dependencyObservable.PropertyChangeData) { var textField = data.object; if (!textField.android) { diff --git a/ui/text-field/text-field.d.ts b/ui/text-field/text-field.d.ts index cefa48b28..b02c3647b 100644 --- a/ui/text-field/text-field.d.ts +++ b/ui/text-field/text-field.d.ts @@ -20,11 +20,6 @@ declare module "ui/text-field" { */ ios: UITextField; - /** - * Gets or sets the text of a text field hint/placeholder. - */ - hint: string; - /** * Gets or sets if a text field is for password entry. */ @@ -35,11 +30,6 @@ declare module "ui/text-field" { * Defines interface for an optional parameter used to create a editable-text-base component. */ export interface Options extends editableTextBase.Options { - /** - * Gets or sets the text of a text field hint/placeholder. - */ - hint?: string; - /** * Gets or sets if a text field is for password entry. */ diff --git a/ui/text-field/text-field.ios.ts b/ui/text-field/text-field.ios.ts index 7ba604185..13336b7ba 100644 --- a/ui/text-field/text-field.ios.ts +++ b/ui/text-field/text-field.ios.ts @@ -4,13 +4,6 @@ import proxy = require("ui/core/proxy"); import textBase = require("ui/text-base"); import enums = require("ui/enums"); -function onHintPropertyChanged(data: dependencyObservable.PropertyChangeData) { - var textField = data.object; - textField.ios.placeholder = data.newValue; -} - -(common.hintProperty.metadata).onSetNativeValue = onHintPropertyChanged; - function onSecurePropertyChanged(data: dependencyObservable.PropertyChangeData) { var textField = data.object; textField.ios.secureTextEntry = data.newValue; @@ -89,4 +82,9 @@ export class TextField extends common.TextField { get ios(): UITextField { return this._ios; } + + public _onHintPropertyChanged(data: dependencyObservable.PropertyChangeData) { + var textField = data.object; + textField.ios.placeholder = data.newValue; + } } \ No newline at end of file diff --git a/ui/text-view/text-view.ios.ts b/ui/text-view/text-view.ios.ts index 679977ca1..fbe5a59b5 100644 --- a/ui/text-view/text-view.ios.ts +++ b/ui/text-view/text-view.ios.ts @@ -21,12 +21,18 @@ class UITextViewDelegateImpl extends NSObject implements UITextViewDelegate { return this; } + public textViewShouldBeginEditing(textView: UITextView): boolean { + this._owner._hideHint(); + return true; + } + public textViewDidEndEditing(textView: UITextView) { if (this._owner.updateTextTrigger === enums.UpdateTextTrigger.focusLost) { this._owner._onPropertyChangedFromNative(textBase.TextBase.textProperty, textView.text); } this._owner.dismissSoftInput(); + this._owner._refreshHintState(this._owner.hint, textView.text); } public textViewDidChange(textView: UITextView) { @@ -69,4 +75,34 @@ export class TextView extends common.TextView { public _onEditablePropertyChanged(data: dependencyObservable.PropertyChangeData) { this._ios.editable = data.newValue; } + + public _onHintPropertyChanged(data: dependencyObservable.PropertyChangeData) { + this._refreshHintState(data.newValue, this.text); + } + + public _onTextPropertyChanged(data: dependencyObservable.PropertyChangeData) { + super._onTextPropertyChanged(data); + this._refreshHintState(this.hint, data.newValue); + } + + public _refreshHintState(hint: string, text: string) { + if (hint && !text) { + this._showHint(hint); + } + else { + this._hideHint(); + } + } + + public _showHint(hint: string) { + this.ios.textColor = this.ios.textColor ? this.ios.textColor.colorWithAlphaComponent(0.22) : UIColor.blackColor().colorWithAlphaComponent(0.22); + this.ios.text = hint + ""; + (this.ios).isShowingHint = true; + } + + public _hideHint() { + this.ios.textColor = this.color ? this.color.ios : null; + this.ios.text = this.text + ""; + (this.ios).isShowingHint = false; + } } \ No newline at end of file