mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-16 03:31:45 +08:00
Merge pull request #1834 from NativeScript/hdeshev/application-css-update
Allow updates to application CSS rules.
This commit is contained in:
@ -6,6 +6,8 @@ import cssSelector = require("ui/styling/css-selector");
|
||||
import * as fileSystemModule from "file-system";
|
||||
import * as styleScopeModule from "ui/styling/style-scope";
|
||||
|
||||
var styleScope: typeof styleScopeModule = undefined;
|
||||
|
||||
var events = new observable.Observable();
|
||||
global.moduleMerge(events, exports);
|
||||
|
||||
@ -22,7 +24,10 @@ export var mainEntry: frame.NavigationEntry;
|
||||
|
||||
export var cssFile: string = "app.css"
|
||||
|
||||
export var cssSelectorsCache: Array<cssSelector.CssSelector> = undefined;
|
||||
export var appSelectors: Array<cssSelector.CssSelector> = [];
|
||||
export var additionalSelectors: Array<cssSelector.CssSelector> = [];
|
||||
export var cssSelectors: Array<cssSelector.CssSelector> = [];
|
||||
export var cssSelectorVersion: number = 0;
|
||||
|
||||
export var resources: any = {};
|
||||
|
||||
@ -50,16 +55,32 @@ export function loadCss(cssFile?: string): Array<cssSelector.CssSelector> {
|
||||
var result: Array<cssSelector.CssSelector>;
|
||||
|
||||
var fs: typeof fileSystemModule = require("file-system");
|
||||
var styleScope: typeof styleScopeModule = require("ui/styling/style-scope");
|
||||
if (!styleScope) {
|
||||
styleScope = require("ui/styling/style-scope");
|
||||
}
|
||||
|
||||
var cssFileName = fs.path.join(fs.knownFolders.currentApp().path, cssFile);
|
||||
if (fs.File.exists(cssFileName)) {
|
||||
var file = fs.File.fromPath(cssFileName);
|
||||
var applicationCss = file.readTextSync();
|
||||
if (applicationCss) {
|
||||
result = styleScope.StyleScope.createSelectorsFromCss(applicationCss, cssFileName);
|
||||
result = parseCss(applicationCss, cssFileName);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeCssSelectors(module: any): void {
|
||||
//HACK: pass the merged module and work with its exported vars.
|
||||
module.cssSelectors = module.appSelectors.slice();
|
||||
module.cssSelectors.push.apply(module.cssSelectors, module.additionalSelectors);
|
||||
module.cssSelectorVersion++;
|
||||
}
|
||||
|
||||
export function parseCss(cssText: string, cssFileName?: string): Array<cssSelector.CssSelector> {
|
||||
if (!styleScope) {
|
||||
styleScope = require("ui/styling/style-scope");
|
||||
}
|
||||
return styleScope.StyleScope.createSelectorsFromCss(cssText, cssFileName);
|
||||
}
|
||||
|
@ -322,7 +322,20 @@ function onConfigurationChanged(context: android.content.Context, intent: androi
|
||||
}
|
||||
|
||||
function loadCss() {
|
||||
typedExports.cssSelectorsCache = typedExports.loadCss(typedExports.cssFile);
|
||||
//HACK: identical to application.ios.ts
|
||||
typedExports.appSelectors = typedExports.loadCss(typedExports.cssFile) || [];
|
||||
if (typedExports.appSelectors.length > 0) {
|
||||
typedExports.mergeCssSelectors(typedExports);
|
||||
}
|
||||
}
|
||||
|
||||
export function addCss(cssText: string) {
|
||||
//HACK: identical to application.ios.ts
|
||||
const parsed = typedExports.parseCss(cssText);
|
||||
if (parsed) {
|
||||
typedExports.additionalSelectors.push.apply(typedExports.additionalSelectors, parsed);
|
||||
typedExports.mergeCssSelectors(typedExports);
|
||||
}
|
||||
}
|
||||
|
||||
global.__onLiveSync = function () {
|
||||
@ -351,4 +364,4 @@ global.__onUncaughtError = function (error: definition.NativeScriptError) {
|
||||
}
|
||||
|
||||
typedExports.notify({ eventName: typedExports.uncaughtErrorEvent, object: appModule.android, android: error });
|
||||
}
|
||||
}
|
||||
|
11
application/application.d.ts
vendored
11
application/application.d.ts
vendored
@ -122,10 +122,19 @@ declare module "application" {
|
||||
*/
|
||||
export var cssFile: string;
|
||||
|
||||
//@private
|
||||
export var appSelectors: Array<cssSelector.CssSelector>;
|
||||
export var additionalSelectors: Array<cssSelector.CssSelector>;
|
||||
/**
|
||||
* Cached css selectors created from the content of the css file.
|
||||
*/
|
||||
export var cssSelectorsCache: Array<cssSelector.CssSelector>;
|
||||
export var cssSelectors: Array<cssSelector.CssSelector>;
|
||||
export var cssSelectorVersion: number;
|
||||
export function parseCss(cssText: string, cssFileName?: string): Array<cssSelector.CssSelector>;
|
||||
export function mergeCssSelectors(module: any): void;
|
||||
//@endprivate
|
||||
|
||||
export function addCss(cssText: string): void;
|
||||
|
||||
/**
|
||||
* Loads css file and parses to a css syntax tree.
|
||||
|
@ -242,7 +242,20 @@ global.__onUncaughtError = function (error: definition.NativeScriptError) {
|
||||
}
|
||||
|
||||
function loadCss() {
|
||||
typedExports.cssSelectorsCache = typedExports.loadCss(typedExports.cssFile);
|
||||
//HACK: identical to application.ios.ts
|
||||
typedExports.appSelectors = typedExports.loadCss(typedExports.cssFile) || [];
|
||||
if (typedExports.appSelectors.length > 0) {
|
||||
typedExports.mergeCssSelectors(typedExports);
|
||||
}
|
||||
}
|
||||
|
||||
export function addCss(cssText: string) {
|
||||
//HACK: identical to application.android.ts
|
||||
const parsed = typedExports.parseCss(cssText);
|
||||
if (parsed) {
|
||||
typedExports.additionalSelectors.push.apply(typedExports.additionalSelectors, parsed);
|
||||
typedExports.mergeCssSelectors(typedExports);
|
||||
}
|
||||
}
|
||||
|
||||
var started: boolean = false;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import TKUnit = require("../../TKUnit");
|
||||
import application = require("application");
|
||||
import buttonModule = require("ui/button");
|
||||
import labelModule = require("ui/label");
|
||||
import pageModule = require("ui/page");
|
||||
@ -52,6 +53,42 @@ export function test_css_is_applied_to_special_properties() {
|
||||
});
|
||||
}
|
||||
|
||||
export function test_applies_css_changes_to_application_rules_before_page_load() {
|
||||
application.addCss(".applicationChangedLabelBefore { color: red; }");
|
||||
const label = new labelModule.Label();
|
||||
label.className = "applicationChangedLabelBefore";
|
||||
label.text = "Red color coming from updated application rules";
|
||||
|
||||
helper.buildUIAndRunTest(label, function (views: Array<viewModule.View>) {
|
||||
helper.assertViewColor(label, "#FF0000");
|
||||
});
|
||||
}
|
||||
|
||||
export function test_applies_css_changes_to_application_rules_after_page_load() {
|
||||
const label1 = new labelModule.Label();
|
||||
label1.text = "Blue color coming from updated application rules";
|
||||
|
||||
helper.buildUIAndRunTest(label1, function (views: Array<viewModule.View>) {
|
||||
application.addCss(".applicationChangedLabelAfter { color: blue; }");
|
||||
label1.className = "applicationChangedLabelAfter";
|
||||
helper.assertViewColor(label1, "#0000FF");
|
||||
});
|
||||
}
|
||||
|
||||
export function test_applies_css_changes_to_application_rules_after_page_load_new_views() {
|
||||
const host = new stackModule.StackLayout();
|
||||
|
||||
helper.buildUIAndRunTest(host, function (views: Array<viewModule.View>) {
|
||||
application.addCss(".applicationChangedLabelAfterNew { color: #00FF00; }");
|
||||
|
||||
const label = new labelModule.Label();
|
||||
label.className = "applicationChangedLabelAfterNew";
|
||||
label.text = "Blue color coming from updated application rules";
|
||||
host.addChild(label);
|
||||
helper.assertViewColor(label, "#00FF00");
|
||||
});
|
||||
}
|
||||
|
||||
// Test for inheritance in different containers
|
||||
export function test_css_is_applied_inside_StackLayout() {
|
||||
var testButton = new buttonModule.Button();
|
||||
|
@ -73,8 +73,7 @@ export class CssSelector {
|
||||
} else {
|
||||
try {
|
||||
view.style._setValue(property, value, observable.ValueSource.Css);
|
||||
}
|
||||
catch (ex) {
|
||||
} catch (ex) {
|
||||
trace.write("Error setting property: " + property.name + " view: " + view + " value: " + value + " " + ex, trace.categories.Style, trace.messageType.error);
|
||||
}
|
||||
}
|
||||
@ -106,6 +105,18 @@ export class CssSelector {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get declarationText(): string {
|
||||
return this.declarations.map((declaration) => `${declaration.property}: ${declaration.value}`).join("; ");
|
||||
}
|
||||
|
||||
public get attrExpressionText(): string {
|
||||
if (this.attrExpression) {
|
||||
return `[${this.attrExpression}]`;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CssTypeSelector extends CssSelector {
|
||||
@ -119,6 +130,9 @@ class CssTypeSelector extends CssSelector {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public toString(): string {
|
||||
return `CssTypeSelector ${this.expression}${this.attrExpressionText} { ${this.declarationText} }`;
|
||||
}
|
||||
}
|
||||
|
||||
function matchesType(expression: string, view: view.View): boolean {
|
||||
@ -153,6 +167,10 @@ class CssIdSelector extends CssSelector {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return `CssIdSelector ${this.expression}${this.attrExpressionText} { ${this.declarationText} }`;
|
||||
}
|
||||
}
|
||||
|
||||
class CssClassSelector extends CssSelector {
|
||||
@ -167,6 +185,10 @@ class CssClassSelector extends CssSelector {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public toString(): string {
|
||||
return `CssClassSelector ${this.expression}${this.attrExpressionText} { ${this.declarationText} }`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CssCompositeSelector extends CssSelector {
|
||||
@ -249,6 +271,10 @@ class CssCompositeSelector extends CssSelector {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return `CssCompositeSelector ${this.expression}${this.attrExpressionText} { ${this.declarationText} }`;
|
||||
}
|
||||
}
|
||||
|
||||
class CssAttrSelector extends CssSelector {
|
||||
@ -259,6 +285,10 @@ class CssAttrSelector extends CssSelector {
|
||||
public matches(view: view.View): boolean {
|
||||
return matchesAttr(this.attrExpression, view);
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return `CssAttrSelector ${this.expression}${this.attrExpressionText} { ${this.declarationText} }`;
|
||||
}
|
||||
}
|
||||
|
||||
function matchesAttr(attrExpression: string, view: view.View): boolean {
|
||||
@ -371,6 +401,10 @@ export class CssVisualStateSelector extends CssSelector {
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return `CssVisualStateSelector ${this.expression}${this.attrExpressionText} { ${this.declarationText} }`;
|
||||
}
|
||||
}
|
||||
|
||||
var HASH = "#";
|
||||
@ -421,6 +455,10 @@ class InlineStyleSelector extends CssSelector {
|
||||
view.style._setValue(property, value, observable.ValueSource.Local);
|
||||
});
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return `InlineStyleSelector ${this.expression}${this.attrExpressionText} { ${this.declarationText} }`;
|
||||
}
|
||||
}
|
||||
|
||||
export function applyInlineSyle(view: view.View, declarations: cssParser.Declaration[]) {
|
||||
|
2
ui/styling/style-scope.d.ts
vendored
2
ui/styling/style-scope.d.ts
vendored
@ -10,7 +10,7 @@ declare module "ui/styling/style-scope" {
|
||||
|
||||
public static createSelectorsFromCss(css: string, cssFileName: string): cssSelector.CssSelector[];
|
||||
public static createSelectorsFromImports(tree: cssParser.SyntaxTree): cssSelector.CssSelector[];
|
||||
public ensureSelectors();
|
||||
public ensureSelectors(): boolean;
|
||||
|
||||
public applySelectors(view: view.View): void
|
||||
public getVisualStates(view: view.View): Object;
|
||||
|
@ -45,36 +45,41 @@ export class StyleScope {
|
||||
|
||||
private _css: string;
|
||||
private _cssFileName: string;
|
||||
private _cssSelectors: Array<cssSelector.CssSelector>;
|
||||
private _mergedCssSelectors: Array<cssSelector.CssSelector>;
|
||||
private _localCssSelectors: Array<cssSelector.CssSelector> = [];
|
||||
private _localCssSelectorVersion: number = 0;
|
||||
private _localCssSelectorsAppliedVersion: number = 0;
|
||||
private _applicationCssSelectorsAppliedVersion: number = 0;
|
||||
|
||||
get css(): string {
|
||||
return this._css;
|
||||
}
|
||||
set css(value: string) {
|
||||
this._css = value;
|
||||
this._cssFileName = undefined;
|
||||
this._cssSelectors = undefined;
|
||||
this._reset();
|
||||
this.setCss(value);
|
||||
}
|
||||
|
||||
public addCss(cssString: string, cssFileName: string): void {
|
||||
public addCss(cssString: string, cssFileName?: string): void {
|
||||
this.setCss(cssString, cssFileName, true);
|
||||
}
|
||||
|
||||
private setCss(cssString: string, cssFileName?: string, append: boolean = false): void {
|
||||
this._css = this._css ? this._css + cssString : cssString;
|
||||
this._cssFileName = cssFileName
|
||||
if (cssFileName) {
|
||||
this._cssFileName = cssFileName
|
||||
}
|
||||
|
||||
this._reset();
|
||||
|
||||
if (!this._cssSelectors) {
|
||||
// Always add app.css when initializing selectors
|
||||
if (application.cssSelectorsCache) {
|
||||
this._cssSelectors = StyleScope._joinCssSelectorsArrays([application.cssSelectorsCache]);
|
||||
}
|
||||
else {
|
||||
this._cssSelectors = new Array<cssSelector.CssSelector>();
|
||||
}
|
||||
const parsedSelectors = StyleScope.createSelectorsFromCss(cssString, cssFileName);
|
||||
if (append) {
|
||||
this._localCssSelectors.push.apply(this._localCssSelectors, parsedSelectors);
|
||||
} else {
|
||||
this._localCssSelectors = parsedSelectors;
|
||||
}
|
||||
|
||||
var selectorsFromFile = StyleScope.createSelectorsFromCss(cssString, cssFileName);
|
||||
this._cssSelectors = StyleScope._joinCssSelectorsArrays([this._cssSelectors, selectorsFromFile]);
|
||||
this._localCssSelectorVersion++;
|
||||
this.ensureSelectors();
|
||||
}
|
||||
|
||||
public static createSelectorsFromCss(css: string, cssFileName: string): cssSelector.CssSelector[] {
|
||||
@ -89,8 +94,7 @@ export class StyleScope {
|
||||
}
|
||||
|
||||
return pageCssSelectors;
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
trace.write("Css styling failed: " + e, trace.categories.Error, trace.messageType.error);
|
||||
}
|
||||
}
|
||||
@ -134,12 +138,23 @@ export class StyleScope {
|
||||
return selectors;
|
||||
}
|
||||
|
||||
public ensureSelectors() {
|
||||
if (!this._cssSelectors && (this._css || application.cssSelectorsCache)) {
|
||||
var applicationCssSelectors = application.cssSelectorsCache ? application.cssSelectorsCache : null;
|
||||
var pageCssSelectors = StyleScope.createSelectorsFromCss(this._css, this._cssFileName);
|
||||
public ensureSelectors(): boolean {
|
||||
let toMerge = []
|
||||
if ((this._applicationCssSelectorsAppliedVersion !== application.cssSelectorVersion) ||
|
||||
(this._localCssSelectorVersion !== this._localCssSelectorsAppliedVersion) ||
|
||||
|
||||
this._cssSelectors = StyleScope._joinCssSelectorsArrays([applicationCssSelectors, pageCssSelectors]);
|
||||
(!this._mergedCssSelectors)) {
|
||||
toMerge.push(application.cssSelectors);
|
||||
this._applicationCssSelectorsAppliedVersion = application.cssSelectorVersion;
|
||||
toMerge.push(this._localCssSelectors);
|
||||
this._localCssSelectorsAppliedVersion = this._localCssSelectorVersion;
|
||||
}
|
||||
|
||||
if (toMerge.length > 0) {
|
||||
this._mergedCssSelectors = StyleScope._joinCssSelectorsArrays(toMerge);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,18 +172,17 @@ export class StyleScope {
|
||||
}
|
||||
|
||||
public applySelectors(view: view.View) {
|
||||
if (!this._cssSelectors) {
|
||||
return;
|
||||
}
|
||||
this.ensureSelectors();
|
||||
|
||||
view.style._beginUpdate();
|
||||
|
||||
var i,
|
||||
selector: cssSelector.CssSelector,
|
||||
matchedStateSelectors = new Array<cssSelector.CssVisualStateSelector>()
|
||||
|
||||
// Go trough all selectors - and directly apply all non-state selectors
|
||||
for (i = 0; i < this._cssSelectors.length; i++) {
|
||||
selector = this._cssSelectors[i];
|
||||
for (i = 0; i < this._mergedCssSelectors.length; i++) {
|
||||
selector = this._mergedCssSelectors[i];
|
||||
if (selector.matches(view)) {
|
||||
if (selector instanceof cssSelector.CssVisualStateSelector) {
|
||||
matchedStateSelectors.push(<cssSelector.CssVisualStateSelector>selector);
|
||||
@ -182,7 +196,6 @@ export class StyleScope {
|
||||
// Create a key for all matched selectors for this element
|
||||
var key: string = "";
|
||||
matchedStateSelectors.forEach((s) => key += s.key + "|");
|
||||
//console.log("Created key: " + key + " for " + matchedStateSelectors.length + " state selectors");
|
||||
|
||||
// Associate the view to the created key
|
||||
this._viewIdToKey[view._domId] = key;
|
||||
|
Reference in New Issue
Block a user