Segmented bar styles

This commit is contained in:
vakrilov
2017-02-01 17:22:15 +02:00
parent 1765afa68c
commit e5f7c7e4d4
7 changed files with 83 additions and 76 deletions

View File

@ -14,7 +14,7 @@ export function checkNativeItemsTextColor(bar: segmentedBarModule.SegmentedBar):
// isValid = bar.color && bar.color.android === t.getCurrentTextColor();
// }
for(let i = 0, itemsLength = bar.items.length; i < itemsLength; i++) {
for (let i = 0, itemsLength = bar.items.length; i < itemsLength; i++) {
let textView = <android.widget.TextView>bar.items[0].nativeView;
isValid = bar.color && bar.color.android === textView.getCurrentTextColor();
}

View File

@ -1,10 +1,10 @@
import * as TKUnit from "../../TKUnit";
import * as segmentedBarTestsNative from "./segmented-bar-tests-native";
import {buildUIAndRunTest} from "../helper";
import {View} from "ui/core/view";
import {BindingOptions} from "ui/core/bindable";
import {Observable} from "data/observable";
import {Color} from "color";
import { buildUIAndRunTest } from "../helper";
import { View } from "ui/core/view";
import { BindingOptions } from "ui/core/bindable";
import { Observable } from "data/observable";
import { Color } from "color";
// >> article-require-segmentedbar-module
import * as segmentedBarModule from "ui/segmented-bar";
@ -77,7 +77,7 @@ export var testWhenItemsAreBoundTheTextColorIsPreserved = function () {
}
segmentedBar.bind(options, model);
TKUnit.assert(segmentedBarTestsNative.checkNativeItemsTextColor(segmentedBar), "Items text color not preserved" + "; Expected: " + segmentedBar.color);
});
}
@ -181,13 +181,13 @@ export var testSelectedIndexChangedIsReisedCorrectlyIfSelectedIndexIsSet = funct
var newIndex;
var segmentedBar = _createSegmentedBar();
segmentedBar.on(segmentedBarModule.SegmentedBar.selectedIndexChangedEvent, (args : segmentedBarModule.SelectedIndexChangedEventData) => {
segmentedBar.on(segmentedBarModule.SegmentedBar.selectedIndexChangedEvent, (args: segmentedBarModule.SelectedIndexChangedEventData) => {
oldIndex = args.oldIndex;
newIndex = args.newIndex;
});
segmentedBar.items = _createItems(10);
buildUIAndRunTest(segmentedBar, function (views: Array<View>) {
var segmentedBar = <segmentedBarModule.SegmentedBar>views[0];
@ -262,7 +262,7 @@ export function test_SettingNumberAsTitleFromXML_DoesNotThrow() {
let item = new segmentedBarModule.SegmentedBarItem();
(<any>item).title = 1;
segmentedBar.items = [item];
buildUIAndRunTest(segmentedBar, function (views: Array<View>) {
TKUnit.assertEqual(item.title, "1");
});

View File

@ -126,7 +126,7 @@ export function getComponentModule(elementName: string, namespace: string, attri
throw new Error(`Css file with path "${cssFilePath}" cannot be found!`);
}
} else {
throw new Error("Css file atribute is valid only for pages!");
throw new Error("Css file attribute is valid only for pages!");
}
}
}

View File

@ -28,7 +28,7 @@ export function _isSet(cssProperty: CssProperty<any, any>, instance: Style): boo
export function _printUnregisteredProperties(): void {
print(symbolPropertyMap);
print(cssSymbolPropertyMap)
print(cssSymbolPropertyMap);
}
const enum ValueSource {
@ -132,11 +132,11 @@ export class Property<T extends ViewBase, U> implements PropertyDescriptor, defi
this.requestLayout();
}
}
}
};
this.get = function (this: T): U {
return key in this ? this[key] : defaultValue;
}
};
this.nativeValueChange = function (owner: T, value: U): void {
const currentValue = key in owner ? owner[key] : defaultValue;
@ -160,7 +160,7 @@ export class Property<T extends ViewBase, U> implements PropertyDescriptor, defi
owner.requestLayout();
}
}
}
};
symbolPropertyMap[key] = this;
}
@ -219,7 +219,7 @@ export class CoercibleProperty<T extends ViewBase, U> implements PropertyDescrip
const originalValue: U = coerceKey in target ? target[coerceKey] : defaultValue;
// need that to make coercing but also fire change events
this.set.call(target, originalValue);
}
};
this.set = function (this: T, value: U): void {
const reset = value === unsetValue;
@ -283,11 +283,11 @@ export class CoercibleProperty<T extends ViewBase, U> implements PropertyDescrip
this.requestLayout();
}
}
}
};
this.get = function (): U {
return key in this ? this[key] : defaultValue;
}
};
this.nativeValueChange = function (owner: T, value: U): void {
const currentValue = key in owner ? owner[key] : defaultValue;
@ -312,7 +312,7 @@ export class CoercibleProperty<T extends ViewBase, U> implements PropertyDescrip
owner.requestLayout();
}
}
}
};
symbolPropertyMap[key] = this;
}
@ -387,7 +387,7 @@ export class InheritedProperty<T extends ViewBase, U> extends Property<T, U> imp
return true;
});
}
}
};
const setInheritedValue = setFunc(ValueSource.Inherited);
this.setInheritedValue = setInheritedValue;
@ -403,6 +403,7 @@ export class CssProperty<T extends Style, U> implements definitions.CssProperty<
public readonly name: string;
public readonly cssName: string;
public readonly cssLocalName: string;
protected readonly cssValueDescriptor: PropertyDescriptor;
protected readonly localValueDescriptor: PropertyDescriptor;
@ -417,8 +418,8 @@ export class CssProperty<T extends Style, U> implements definitions.CssProperty<
const name = options.name;
this.name = name;
const cssName = `css-${options.cssName}`;
this.cssName = cssName;
this.cssName = `css-${options.cssName}`;
this.cssLocalName = options.cssName;
const key = Symbol(name + ":propertyKey");
this.key = key;
@ -593,6 +594,9 @@ export class CssProperty<T extends Style, U> implements definitions.CssProperty<
this.registered = true;
Object.defineProperty(cls.prototype, this.name, this.localValueDescriptor);
Object.defineProperty(cls.prototype, this.cssName, this.cssValueDescriptor);
if (this.cssLocalName !== this.cssName) {
Object.defineProperty(cls.prototype, this.cssLocalName, this.localValueDescriptor);
}
}
}
@ -634,7 +638,7 @@ export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U>
if (reset) {
// If unsetValue - we want to reset this property.
let parent = view.parent;
let style = parent ? parent.style : null
let style = parent ? parent.style : null;
// If we have parent and it has non-default value we use as our inherited value.
if (style && style[sourceKey] > ValueSource.Default) {
newValue = style[name];
@ -712,7 +716,7 @@ export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U>
return true;
});
}
}
};
const setDefaultFunc = setFunc(ValueSource.Default);
const setInheritedFunc = setFunc(ValueSource.Inherited);
@ -731,6 +735,7 @@ export class ShorthandProperty<T extends Style, P> implements definitions.Shorth
public readonly key: symbol;
public readonly name: string;
public readonly cssName: string;
public readonly cssLocalName: string;
protected readonly cssValueDescriptor: PropertyDescriptor;
protected readonly localValueDescriptor: PropertyDescriptor;
@ -739,14 +744,13 @@ export class ShorthandProperty<T extends Style, P> implements definitions.Shorth
public readonly sourceKey: symbol;
constructor(options: definitions.ShorthandPropertyOptions<P>) {
const name = options.name;
this.name = name;
this.name = options.name;
const key = Symbol(name + ":propertyKey");
const key = Symbol(this.name + ":propertyKey");
this.key = key;
const cssName = `css-${options.cssName}`;
this.cssName = cssName;
this.cssName = `css-${options.cssName}`;
this.cssLocalName = `${options.cssName}`;
const converter = options.converter;
@ -792,6 +796,9 @@ export class ShorthandProperty<T extends Style, P> implements definitions.Shorth
this.registered = true;
Object.defineProperty(cls.prototype, this.name, this.localValueDescriptor);
Object.defineProperty(cls.prototype, this.cssName, this.cssValueDescriptor);
if (this.cssLocalName !== this.cssName) {
Object.defineProperty(cls.prototype, this.cssLocalName, this.localValueDescriptor);
}
}
}
@ -958,5 +965,5 @@ export function makeParser<T>(isValid: (value: any) => boolean): (value: any) =>
} else {
throw new Error("Invalid value: " + value);
}
}
};
}

View File

@ -1,7 +1,7 @@
import { SegmentedBar as SegmentedBarDefinition, SegmentedBarItem as SegmentedBarItemDefinition, SelectedIndexChangedEventData } from "ui/segmented-bar";
import {
ViewBase, View, AddChildFromBuilder, AddArrayFromBuilder,
Property, CoercibleProperty, CssProperty, Color, Style
Property, CoercibleProperty, InheritedCssProperty, Color, Style
} from "ui/core/view";
export * from "ui/core/view";
@ -45,7 +45,9 @@ export abstract class SegmentedBarBase extends View implements SegmentedBarDefin
if (!this.items) {
this.items = new Array<SegmentedBarItemBase>();
}
this.items.push(<SegmentedBarItemBase>value);
let item = <SegmentedBarItemBase>value;
this.items.push(item);
this._addView(item);
selectedIndexProperty.coerce(this);
}
}
@ -111,5 +113,5 @@ export const itemsProperty = new Property<SegmentedBarBase, SegmentedBarItemDefi
});
itemsProperty.register(SegmentedBarBase);
export const selectedBackgroundColorProperty = new CssProperty<Style, Color>({ name: "selectedBackgroundColor", cssName: "selected-background-color", equalityComparer: Color.equals, valueConverter: (v) => new Color(v) })
export const selectedBackgroundColorProperty = new InheritedCssProperty<Style, Color>({ name: "selectedBackgroundColor", cssName: "selected-background-color", equalityComparer: Color.equals, valueConverter: (v) => new Color(v) });
selectedBackgroundColorProperty.register(Style);

View File

@ -8,6 +8,7 @@ export * from "./segmented-bar-common";
const R_ID_TABS = 0x01020013;
const R_ID_TABCONTENT = 0x01020011;
const R_ATTR_STATE_SELECTED = 0x010100a1;
const TITLE_TEXT_VIEW_ID = 16908310; // http://developer.android.com/reference/android/R.id.html#title
let apiLevel: number;
// TODO: Move this into widgets.
@ -56,9 +57,13 @@ export class SegmentedBarItem extends SegmentedBarItemBase {
return this._textView;
}
public setNativeView(textView: android.widget.TextView): void {
this._textView = textView;
if (textView) {
public setupNativeView(tabIndex: number): void {
// TabHost.TabSpec.setIndicator DOES NOT WORK once the title has been set.
// http://stackoverflow.com/questions/2935781/modify-tab-indicator-dynamically-in-android
const titleTextView = <android.widget.TextView>this.parent.android.getTabWidget().getChildAt(tabIndex).findViewById(TITLE_TEXT_VIEW_ID);
this._textView = titleTextView;
if (titleTextView) {
applyNativeSetters(this);
if (this.titleDirty) {
this._update();
@ -68,15 +73,6 @@ export class SegmentedBarItem extends SegmentedBarItemBase {
private titleDirty: boolean;
public _update(): void {
// if (this._parent && this._parent.android) {
// // TabHost.TabSpec.setIndicator DOES NOT WORK once the title has been set.
// // http://stackoverflow.com/questions/2935781/modify-tab-indicator-dynamically-in-android
// const tabIndex = this._parent.items.indexOf(this);
// const titleTextViewId = 16908310; // http://developer.android.com/reference/android/R.id.html#title
// const titleTextView = <android.widget.TextView>this._parent.android.getTabWidget().getChildAt(tabIndex).findViewById(titleTextViewId);
// titleTextView.setText(this.title || "");
// }
const tv = this._textView;
if (tv) {
let title = this.title;
@ -168,15 +164,12 @@ class TabContentFactory extends java.lang.Object implements android.widget.TabHo
let owner = this.owner.get();
if (owner) {
let tv = new android.widget.TextView(owner._context);
let index = parseInt(tag);
// This is collapsed by default and made visibile
// This is collapsed by default and made visible
// by android when TabItem becomes visible/selected.
// TODO: Try commenting visigility change.
// TODO: Try commenting visibility change.
tv.setVisibility(android.view.View.GONE);
tv.setMaxLines(1);
tv.setEllipsize(android.text.TextUtils.TruncateAt.END);
(<SegmentedBarItem>owner.items[index]).setNativeView(tv);
return tv;
} else {
throw new Error(`Invalid owner: ${this.owner}`);
@ -233,6 +226,7 @@ export class SegmentedBar extends SegmentedBarBase {
let tabHost = this.android;
this._addingTab = true;
tabHost.addTab(tab);
tabItem.setupNativeView(index);
this._addingTab = false;
}

View File

@ -8,7 +8,7 @@ import * as application from "application";
import * as kam from "ui/animation/keyframe-animation";
let keyframeAnimationModule: typeof kam;
function ensureKeyframeAnimationModule() {
if (!keyframeAnimationModule){
if (!keyframeAnimationModule) {
keyframeAnimationModule = require("ui/animation/keyframe-animation");
}
}
@ -16,7 +16,7 @@ function ensureKeyframeAnimationModule() {
import * as capm from "./css-animation-parser";
let cssAnimationParserModule: typeof capm;
function ensureCssAnimationParserModule() {
if (!cssAnimationParserModule){
if (!cssAnimationParserModule) {
cssAnimationParserModule = require("./css-animation-parser");
}
}
@ -48,15 +48,16 @@ export class CssState {
private applyDescriptors(view: ViewBase, ruleset: RuleSet): void {
let style = view.style;
ruleset.declarations.forEach(d => {
let name = `css-${d.property}`;
if (name in style) {
try {
style[name] = d.value;
} catch (e) {
traceWrite(`Failed to apply property [${d.property}] with value [${d.value}] to ${view}. ${e}`, traceCategories.Error, traceMessageType.error)
try {
// Use the "css-" prefixed name, so that CSS value source is set.
let cssPropName = `css-${d.property}`;
if (cssPropName in style) {
style[cssPropName] = d.value;
} else {
view[d.property] = d.value;
}
} else {
view[d.property] = d.value;
} catch (e) {
traceWrite(`Failed to apply property [${d.property}] with value [${d.value}] to ${view}. ${e}`, traceCategories.Error, traceMessageType.error);
}
});
@ -228,7 +229,7 @@ export class StyleScope {
(<Keyframes[]>nodes.filter(isKeyframe)).forEach(node => keyframes[node.name] = node);
let rulesets = fromAstNodes(nodes);
if (rulesets && rulesets.length){
if (rulesets && rulesets.length) {
ensureCssAnimationParserModule();
rulesets.forEach(rule => rule[animationsSymbol] = cssAnimationParserModule.CssAnimationParser.keyframeAnimationsFromCSSDeclarations(rule.declarations));
}
@ -262,7 +263,7 @@ export class StyleScope {
}
}
export function resolveFileNameFromUrl(url: string, appDirectory: string, fileExists: (string) => boolean): string {
export function resolveFileNameFromUrl(url: string, appDirectory: string, fileExists: (name: string) => boolean): string {
let fileName: string = typeof url === "string" ? url.trim() : "";
if (fileName.indexOf("~/") === 0) {
@ -282,21 +283,24 @@ export function resolveFileNameFromUrl(url: string, appDirectory: string, fileEx
return null;
}
export function applyInlineStyle(view: ViewBase, style: string) {
try {
let localStyle = `local { ${style} }`;
let inlineRuleSet = StyleScope.createSelectorsFromCss(localStyle, null, {});
let inlineSelector = new InlineSelector(inlineRuleSet[0]);
view.inlineStyleSelector = inlineSelector;
if (view._cssState) {
view._cssState.apply();
} else {
let styleScope = new StyleScope();
styleScope.applySelectors(view);
export function applyInlineStyle(view: ViewBase, styleStr: string) {
let localStyle = `local { ${styleStr} }`;
let inlineRuleSet = StyleScope.createSelectorsFromCss(localStyle, null, {});
const style = view.style;
inlineRuleSet[0].declarations.forEach(d => {
// Use the actual property name so that a local value is set.
let name = d.property;
try {
if (name in style) {
style[name] = d.value;
} else {
view[name] = d.value;
}
} catch (e) {
traceWrite(`Failed to apply property [${d.property}] with value [${d.value}] to ${view}. ${e}`, traceCategories.Error, traceMessageType.error);
}
} catch (ex) {
traceWrite("Applying local style failed: " + ex, traceCategories.Error, traceMessageType.error);
}
});
}
function isKeyframe(node: Node): node is Keyframes {