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(); // 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; let textView = <android.widget.TextView>bar.items[0].nativeView;
isValid = bar.color && bar.color.android === textView.getCurrentTextColor(); isValid = bar.color && bar.color.android === textView.getCurrentTextColor();
} }

View File

@ -1,10 +1,10 @@
import * as TKUnit from "../../TKUnit"; import * as TKUnit from "../../TKUnit";
import * as segmentedBarTestsNative from "./segmented-bar-tests-native"; import * as segmentedBarTestsNative from "./segmented-bar-tests-native";
import {buildUIAndRunTest} from "../helper"; import { buildUIAndRunTest } from "../helper";
import {View} from "ui/core/view"; import { View } from "ui/core/view";
import {BindingOptions} from "ui/core/bindable"; import { BindingOptions } from "ui/core/bindable";
import {Observable} from "data/observable"; import { Observable } from "data/observable";
import {Color} from "color"; import { Color } from "color";
// >> article-require-segmentedbar-module // >> article-require-segmentedbar-module
import * as segmentedBarModule from "ui/segmented-bar"; import * as segmentedBarModule from "ui/segmented-bar";
@ -181,7 +181,7 @@ export var testSelectedIndexChangedIsReisedCorrectlyIfSelectedIndexIsSet = funct
var newIndex; var newIndex;
var segmentedBar = _createSegmentedBar(); var segmentedBar = _createSegmentedBar();
segmentedBar.on(segmentedBarModule.SegmentedBar.selectedIndexChangedEvent, (args : segmentedBarModule.SelectedIndexChangedEventData) => { segmentedBar.on(segmentedBarModule.SegmentedBar.selectedIndexChangedEvent, (args: segmentedBarModule.SelectedIndexChangedEventData) => {
oldIndex = args.oldIndex; oldIndex = args.oldIndex;
newIndex = args.newIndex; newIndex = args.newIndex;
}); });

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!`); throw new Error(`Css file with path "${cssFilePath}" cannot be found!`);
} }
} else { } 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 { export function _printUnregisteredProperties(): void {
print(symbolPropertyMap); print(symbolPropertyMap);
print(cssSymbolPropertyMap) print(cssSymbolPropertyMap);
} }
const enum ValueSource { const enum ValueSource {
@ -132,11 +132,11 @@ export class Property<T extends ViewBase, U> implements PropertyDescriptor, defi
this.requestLayout(); this.requestLayout();
} }
} }
} };
this.get = function (this: T): U { this.get = function (this: T): U {
return key in this ? this[key] : defaultValue; return key in this ? this[key] : defaultValue;
} };
this.nativeValueChange = function (owner: T, value: U): void { this.nativeValueChange = function (owner: T, value: U): void {
const currentValue = key in owner ? owner[key] : defaultValue; const currentValue = key in owner ? owner[key] : defaultValue;
@ -160,7 +160,7 @@ export class Property<T extends ViewBase, U> implements PropertyDescriptor, defi
owner.requestLayout(); owner.requestLayout();
} }
} }
} };
symbolPropertyMap[key] = this; 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; const originalValue: U = coerceKey in target ? target[coerceKey] : defaultValue;
// need that to make coercing but also fire change events // need that to make coercing but also fire change events
this.set.call(target, originalValue); this.set.call(target, originalValue);
} };
this.set = function (this: T, value: U): void { this.set = function (this: T, value: U): void {
const reset = value === unsetValue; const reset = value === unsetValue;
@ -283,11 +283,11 @@ export class CoercibleProperty<T extends ViewBase, U> implements PropertyDescrip
this.requestLayout(); this.requestLayout();
} }
} }
} };
this.get = function (): U { this.get = function (): U {
return key in this ? this[key] : defaultValue; return key in this ? this[key] : defaultValue;
} };
this.nativeValueChange = function (owner: T, value: U): void { this.nativeValueChange = function (owner: T, value: U): void {
const currentValue = key in owner ? owner[key] : defaultValue; const currentValue = key in owner ? owner[key] : defaultValue;
@ -312,7 +312,7 @@ export class CoercibleProperty<T extends ViewBase, U> implements PropertyDescrip
owner.requestLayout(); owner.requestLayout();
} }
} }
} };
symbolPropertyMap[key] = this; symbolPropertyMap[key] = this;
} }
@ -387,7 +387,7 @@ export class InheritedProperty<T extends ViewBase, U> extends Property<T, U> imp
return true; return true;
}); });
} }
} };
const setInheritedValue = setFunc(ValueSource.Inherited); const setInheritedValue = setFunc(ValueSource.Inherited);
this.setInheritedValue = setInheritedValue; this.setInheritedValue = setInheritedValue;
@ -403,6 +403,7 @@ export class CssProperty<T extends Style, U> implements definitions.CssProperty<
public readonly name: string; public readonly name: string;
public readonly cssName: string; public readonly cssName: string;
public readonly cssLocalName: string;
protected readonly cssValueDescriptor: PropertyDescriptor; protected readonly cssValueDescriptor: PropertyDescriptor;
protected readonly localValueDescriptor: PropertyDescriptor; protected readonly localValueDescriptor: PropertyDescriptor;
@ -417,8 +418,8 @@ export class CssProperty<T extends Style, U> implements definitions.CssProperty<
const name = options.name; const name = options.name;
this.name = name; this.name = name;
const cssName = `css-${options.cssName}`; this.cssName = `css-${options.cssName}`;
this.cssName = cssName; this.cssLocalName = options.cssName;
const key = Symbol(name + ":propertyKey"); const key = Symbol(name + ":propertyKey");
this.key = key; this.key = key;
@ -593,6 +594,9 @@ export class CssProperty<T extends Style, U> implements definitions.CssProperty<
this.registered = true; this.registered = true;
Object.defineProperty(cls.prototype, this.name, this.localValueDescriptor); Object.defineProperty(cls.prototype, this.name, this.localValueDescriptor);
Object.defineProperty(cls.prototype, this.cssName, this.cssValueDescriptor); 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 (reset) {
// If unsetValue - we want to reset this property. // If unsetValue - we want to reset this property.
let parent = view.parent; 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 we have parent and it has non-default value we use as our inherited value.
if (style && style[sourceKey] > ValueSource.Default) { if (style && style[sourceKey] > ValueSource.Default) {
newValue = style[name]; newValue = style[name];
@ -712,7 +716,7 @@ export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U>
return true; return true;
}); });
} }
} };
const setDefaultFunc = setFunc(ValueSource.Default); const setDefaultFunc = setFunc(ValueSource.Default);
const setInheritedFunc = setFunc(ValueSource.Inherited); 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 key: symbol;
public readonly name: string; public readonly name: string;
public readonly cssName: string; public readonly cssName: string;
public readonly cssLocalName: string;
protected readonly cssValueDescriptor: PropertyDescriptor; protected readonly cssValueDescriptor: PropertyDescriptor;
protected readonly localValueDescriptor: PropertyDescriptor; protected readonly localValueDescriptor: PropertyDescriptor;
@ -739,14 +744,13 @@ export class ShorthandProperty<T extends Style, P> implements definitions.Shorth
public readonly sourceKey: symbol; public readonly sourceKey: symbol;
constructor(options: definitions.ShorthandPropertyOptions<P>) { constructor(options: definitions.ShorthandPropertyOptions<P>) {
const name = options.name; this.name = options.name;
this.name = name;
const key = Symbol(name + ":propertyKey"); const key = Symbol(this.name + ":propertyKey");
this.key = key; this.key = key;
const cssName = `css-${options.cssName}`; this.cssName = `css-${options.cssName}`;
this.cssName = cssName; this.cssLocalName = `${options.cssName}`;
const converter = options.converter; const converter = options.converter;
@ -792,6 +796,9 @@ export class ShorthandProperty<T extends Style, P> implements definitions.Shorth
this.registered = true; this.registered = true;
Object.defineProperty(cls.prototype, this.name, this.localValueDescriptor); Object.defineProperty(cls.prototype, this.name, this.localValueDescriptor);
Object.defineProperty(cls.prototype, this.cssName, this.cssValueDescriptor); 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 { } else {
throw new Error("Invalid value: " + value); 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 { SegmentedBar as SegmentedBarDefinition, SegmentedBarItem as SegmentedBarItemDefinition, SelectedIndexChangedEventData } from "ui/segmented-bar";
import { import {
ViewBase, View, AddChildFromBuilder, AddArrayFromBuilder, ViewBase, View, AddChildFromBuilder, AddArrayFromBuilder,
Property, CoercibleProperty, CssProperty, Color, Style Property, CoercibleProperty, InheritedCssProperty, Color, Style
} from "ui/core/view"; } from "ui/core/view";
export * from "ui/core/view"; export * from "ui/core/view";
@ -45,7 +45,9 @@ export abstract class SegmentedBarBase extends View implements SegmentedBarDefin
if (!this.items) { if (!this.items) {
this.items = new Array<SegmentedBarItemBase>(); this.items = new Array<SegmentedBarItemBase>();
} }
this.items.push(<SegmentedBarItemBase>value); let item = <SegmentedBarItemBase>value;
this.items.push(item);
this._addView(item);
selectedIndexProperty.coerce(this); selectedIndexProperty.coerce(this);
} }
} }
@ -111,5 +113,5 @@ export const itemsProperty = new Property<SegmentedBarBase, SegmentedBarItemDefi
}); });
itemsProperty.register(SegmentedBarBase); 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); selectedBackgroundColorProperty.register(Style);

View File

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

View File

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