mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-16 03:31:45 +08:00
Chrome devtools elements tab support for Android (#4351)
* Enable chrome-devtools elemets tab * Trigger updates when property is chaned form native * Tslint fixes * Don't run dom-elemet tests in IOS * fix tests * Create package.json * Update package.json * domNode changed to field for performance
This commit is contained in:

committed by
GitHub

parent
b7c61cad96
commit
f2462158fb
13
apps/app/devtools-app/app.css
Normal file
13
apps/app/devtools-app/app.css
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.btn1 {
|
||||||
|
background-color: lightgreen;
|
||||||
|
color: coral;
|
||||||
|
font-size: 20;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn2 {
|
||||||
|
background-color: coral;
|
||||||
|
color: lightgreen;
|
||||||
|
font-size: 24;
|
||||||
|
font-family: serif;
|
||||||
|
}
|
6
apps/app/devtools-app/app.ts
Normal file
6
apps/app/devtools-app/app.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import * as application from "tns-core-modules/application";
|
||||||
|
|
||||||
|
// Needed only for build infrastructure
|
||||||
|
application.setCssFileName("devtools-app/app.css");
|
||||||
|
|
||||||
|
application.start({ moduleName: "devtools-app/main-page" });
|
36
apps/app/devtools-app/main-page.ts
Normal file
36
apps/app/devtools-app/main-page.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import * as frame from "tns-core-modules/ui/frame";
|
||||||
|
import { Label } from "tns-core-modules/ui/label";
|
||||||
|
import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout";
|
||||||
|
// import { DOMNode } from "tns-core-modules/debugger/dom-node";
|
||||||
|
|
||||||
|
export function print(args) {
|
||||||
|
// const node = new DOMNode(frame.topmost());
|
||||||
|
// console.dir(node.toJSON());
|
||||||
|
|
||||||
|
// const btn = args.object.page.getViewById("btn");
|
||||||
|
// btn.ensureDomNode();
|
||||||
|
// console.dir(btn.domNode.getComputedProperties());
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
export function add(args) {
|
||||||
|
const container = args.object.page.getViewById("container");
|
||||||
|
const lbl = new Label();
|
||||||
|
lbl.text = "label " + i++;
|
||||||
|
container.addChild(lbl);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function remove(args) {
|
||||||
|
const container = <StackLayout>args.object.page.getViewById("container");
|
||||||
|
const lbl = container.getChildAt(container.getChildrenCount() - 1);
|
||||||
|
container.removeChild(lbl);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function navigate() {
|
||||||
|
frame.topmost().navigate("gallery-app/main-page");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function change(args){
|
||||||
|
args.object.text = "hi " + Math.random();
|
||||||
|
|
||||||
|
}
|
29
apps/app/devtools-app/main-page.xml
Normal file
29
apps/app/devtools-app/main-page.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<Page navigatedTo="print">
|
||||||
|
<GridLayout rows="* * *" columns="* * *">
|
||||||
|
|
||||||
|
<Button id="btn"
|
||||||
|
customProp="custom"
|
||||||
|
text="hi there"
|
||||||
|
fontSize="30"
|
||||||
|
row="1"
|
||||||
|
col="1"
|
||||||
|
color="red"
|
||||||
|
backgroundColor="lightgreen"
|
||||||
|
class="btn"
|
||||||
|
tap="change"/>
|
||||||
|
<Button text="class: btn1" class="btn1" row="1" col="0"/>
|
||||||
|
<Button text="class: btn2" class="btn2" row="1" col="2"/>
|
||||||
|
|
||||||
|
<Button text="add" tap="add" />
|
||||||
|
<Button text="remove" tap="remove" col="2" />
|
||||||
|
|
||||||
|
<Button text="move beach" tap="navigate" col="1" />
|
||||||
|
<StackLayout colSpan="3" row="2" id="container">
|
||||||
|
<TextField hint="hint" text="123" />
|
||||||
|
<TextField hint="hint" text="456" />
|
||||||
|
|
||||||
|
|
||||||
|
</StackLayout>
|
||||||
|
</GridLayout>
|
||||||
|
</Page>
|
334
tests/app/debugger/dom-node-tests.ts
Normal file
334
tests/app/debugger/dom-node-tests.ts
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
import { assert, assertEqual } from "../TKUnit";
|
||||||
|
import { DOMNode } from "tns-core-modules/debugger/dom-node";
|
||||||
|
import { attachInspectorCallbacks } from "tns-core-modules/debugger/devtools-elements";
|
||||||
|
import { Inspector } from "tns-core-modules/debugger/devtools-elements";
|
||||||
|
import { unsetValue } from "tns-core-modules/ui/core/properties";
|
||||||
|
import { Button } from "tns-core-modules/ui/button";
|
||||||
|
import { Slider } from "tns-core-modules/ui/slider";
|
||||||
|
import { Label } from "tns-core-modules/ui/label";
|
||||||
|
import { textProperty } from "tns-core-modules/ui/text-base";
|
||||||
|
import { TextView } from "tns-core-modules/ui/text-view";
|
||||||
|
import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout";
|
||||||
|
|
||||||
|
let originalInspectorGlobal: Inspector;
|
||||||
|
let currentInspector: Inspector;
|
||||||
|
function getTestInspector(): Inspector {
|
||||||
|
let inspector = {
|
||||||
|
getDocument(): string { return ""; },
|
||||||
|
removeNode(nodeId: number): void { /* */ },
|
||||||
|
getComputedStylesForNode(nodeId: number): string { return ""; },
|
||||||
|
setAttributeAsText(nodeId: number, text: string, name: string): void { /* */},
|
||||||
|
|
||||||
|
childNodeInserted(parentId: number, lastId: number, nodeStr: string): void { /* to be replaced */ },
|
||||||
|
childNodeRemoved(parentId: number, nodeId: number): void { /* to be replaced */ },
|
||||||
|
attributeModified(nodeId: number, attrName: string, attrValue: string) { /* to be replaced */ },
|
||||||
|
attributeRemoved(nodeId: number, attrName: string) { /* to be replaced */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
attachInspectorCallbacks(inspector);
|
||||||
|
|
||||||
|
return inspector;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setUp(): void {
|
||||||
|
originalInspectorGlobal = global.__inspector;
|
||||||
|
currentInspector = getTestInspector();
|
||||||
|
global.__inspector = currentInspector;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tearDown(): void {
|
||||||
|
global.__inspector = originalInspectorGlobal;
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertAttribute(domNode: DOMNode, name: string, value: any) {
|
||||||
|
const propIdx = domNode.attributes.indexOf(name);
|
||||||
|
assert(propIdx >= 0, `Attribute ${name} not found`);
|
||||||
|
assertEqual(domNode.attributes[propIdx + 1], value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_custom_attribute_is_reported_in_dom_node() {
|
||||||
|
const btn = new Button();
|
||||||
|
btn["test_prop"] = "test_value";
|
||||||
|
btn.ensureDomNode();
|
||||||
|
const domNode = btn.domNode;
|
||||||
|
assertAttribute(domNode, "test_prop", "test_value");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_custom__falsy_attribute_is_reported_in_dom_node() {
|
||||||
|
const btn = new Button();
|
||||||
|
btn["test_prop_null"] = null;
|
||||||
|
btn["test_prop_0"] = 0;
|
||||||
|
btn["test_prop_undefined"] = undefined;
|
||||||
|
btn["test_prop_empty_string"] = "";
|
||||||
|
|
||||||
|
btn.ensureDomNode();
|
||||||
|
const domNode = btn.domNode;
|
||||||
|
assertAttribute(domNode, "test_prop_null", null + "");
|
||||||
|
assertAttribute(domNode, "test_prop_0", 0 + "");
|
||||||
|
assertAttribute(domNode, "test_prop_undefined", undefined + "");
|
||||||
|
assertAttribute(domNode, "test_prop_empty_string", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_property_is_reported_in_dom_node() {
|
||||||
|
const btn = new Button();
|
||||||
|
btn.text = "test_value";
|
||||||
|
btn.ensureDomNode();
|
||||||
|
const domNode = btn.domNode;
|
||||||
|
assertAttribute(domNode, "text", "test_value");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_childNodeInserted_in_dom_node() {
|
||||||
|
let childNodeInsertedCalled = false;
|
||||||
|
let actualParentId = 0;
|
||||||
|
let expectedParentId = 0;
|
||||||
|
|
||||||
|
currentInspector.childNodeInserted = (parentId, lastNodeId, node) => {
|
||||||
|
childNodeInsertedCalled = true;
|
||||||
|
actualParentId = parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stack = new StackLayout();
|
||||||
|
stack.ensureDomNode();
|
||||||
|
expectedParentId = stack._domId;
|
||||||
|
|
||||||
|
const btn1 = new Button();
|
||||||
|
btn1.text = "button1";
|
||||||
|
stack.addChild(btn1);
|
||||||
|
|
||||||
|
assert(childNodeInsertedCalled, "global.__inspector.childNodeInserted not called.");
|
||||||
|
assertEqual(actualParentId, expectedParentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_childNodeInserted_at_index_in_dom_node() {
|
||||||
|
const stack = new StackLayout();
|
||||||
|
stack.ensureDomNode();
|
||||||
|
|
||||||
|
// child index 0
|
||||||
|
const btn1 = new Button();
|
||||||
|
btn1.text = "button1";
|
||||||
|
stack.addChild(btn1);
|
||||||
|
|
||||||
|
// child index 1
|
||||||
|
const btn2 = new Button();
|
||||||
|
btn2.text = "button2";
|
||||||
|
stack.addChild(btn2);
|
||||||
|
|
||||||
|
// child index 2
|
||||||
|
const btn3 = new Button();
|
||||||
|
btn3.text = "button3";
|
||||||
|
stack.addChild(btn3);
|
||||||
|
|
||||||
|
const lbl = new Label();
|
||||||
|
lbl.text = "label me this";
|
||||||
|
|
||||||
|
let called = false;
|
||||||
|
currentInspector.childNodeInserted = (parentId, lastNodeId, node) => {
|
||||||
|
assertEqual(lastNodeId, btn1._domId, "Child inserted at index 1's previous sibling does not match.");
|
||||||
|
assertEqual(JSON.parse(node).nodeId, lbl._domId, "Child id doesn't match");
|
||||||
|
called = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.insertChild(lbl, 1);
|
||||||
|
assert(called, "childNodeInserted not called");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_childNodeRemoved_in_dom_node() {
|
||||||
|
let childNodeRemovedCalled = false;
|
||||||
|
let actualRemovedNodeId = 0;
|
||||||
|
let expectedRemovedNodeId = 0;
|
||||||
|
|
||||||
|
currentInspector.childNodeRemoved = (parentId, nodeId) => {
|
||||||
|
childNodeRemovedCalled = true;
|
||||||
|
actualRemovedNodeId = nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stack = new StackLayout();
|
||||||
|
stack.ensureDomNode();
|
||||||
|
|
||||||
|
const btn1 = new Button();
|
||||||
|
btn1.text = "button1";
|
||||||
|
expectedRemovedNodeId = btn1._domId;
|
||||||
|
stack.addChild(btn1);
|
||||||
|
|
||||||
|
const btn2 = new Button();
|
||||||
|
btn2.text = "button2";
|
||||||
|
stack.addChild(btn2);
|
||||||
|
|
||||||
|
stack.removeChild(btn1);
|
||||||
|
console.log("btn2: " + btn2);
|
||||||
|
|
||||||
|
assert(childNodeRemovedCalled, "global.__inspector.childNodeRemoved not called.");
|
||||||
|
assertEqual(actualRemovedNodeId, expectedRemovedNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_falsy_property_is_reported_in_dom_node() {
|
||||||
|
const btn = new Button();
|
||||||
|
btn.text = null;
|
||||||
|
btn.ensureDomNode();
|
||||||
|
const domNode = btn.domNode;
|
||||||
|
assertAttribute(domNode, "text", "null");
|
||||||
|
|
||||||
|
btn.text = undefined;
|
||||||
|
domNode.loadAttributes();
|
||||||
|
assertAttribute(domNode, "text", "undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_property_change_calls_attributeModified() {
|
||||||
|
const btn = new Button();
|
||||||
|
btn.ensureDomNode();
|
||||||
|
const domNode = btn.domNode;
|
||||||
|
|
||||||
|
let callbackCalled = false;
|
||||||
|
currentInspector.attributeModified = (nodeId: number, attrName: string, attrValue: string) => {
|
||||||
|
assertEqual(nodeId, domNode.nodeId, "nodeId");
|
||||||
|
assertEqual(attrName, "text", "attrName");
|
||||||
|
assertEqual(attrValue, "new value", "attrValue");
|
||||||
|
callbackCalled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.text = "new value";
|
||||||
|
|
||||||
|
assert(callbackCalled, "attributeModified not called");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_property_change_from_native_calls_attributeModified() {
|
||||||
|
const tv = new TextView();
|
||||||
|
tv.ensureDomNode();
|
||||||
|
const domNode = tv.domNode;
|
||||||
|
|
||||||
|
let callbackCalled = false;
|
||||||
|
currentInspector.attributeModified = (nodeId: number, attrName: string, attrValue: string) => {
|
||||||
|
assertEqual(nodeId, domNode.nodeId, "nodeId");
|
||||||
|
assertEqual(attrName, "text", "attrName");
|
||||||
|
assertEqual(attrValue, "new value", "attrValue");
|
||||||
|
callbackCalled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
textProperty.nativeValueChange(tv, "new value");
|
||||||
|
|
||||||
|
assert(callbackCalled, "attributeModified not called");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_property_reset_calls_attributeRemoved() {
|
||||||
|
const btn = new Button();
|
||||||
|
btn.text = "some value";
|
||||||
|
btn.ensureDomNode();
|
||||||
|
const domNode = btn.domNode;
|
||||||
|
|
||||||
|
let callbackCalled = false;
|
||||||
|
currentInspector.attributeRemoved = (nodeId: number, attrName: string) => {
|
||||||
|
assertEqual(nodeId, domNode.nodeId, "nodeId");
|
||||||
|
assertEqual(attrName, "text", "attrName");
|
||||||
|
callbackCalled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.text = unsetValue;
|
||||||
|
|
||||||
|
assert(callbackCalled, "attributeRemoved not called");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_coercible_property_change_calls_attributeModified() {
|
||||||
|
const slider = new Slider();
|
||||||
|
slider.ensureDomNode();
|
||||||
|
const domNode = slider.domNode;
|
||||||
|
|
||||||
|
let callbackCalled = false;
|
||||||
|
currentInspector.attributeModified = (nodeId: number, attrName: string, attrValue: string) => {
|
||||||
|
assertEqual(nodeId, domNode.nodeId, "nodeId");
|
||||||
|
assertEqual(attrName, "value", "attrName");
|
||||||
|
assertEqual(attrValue, "10", "attrValue");
|
||||||
|
callbackCalled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
slider.value = 10;
|
||||||
|
|
||||||
|
assert(callbackCalled, "attributeModified not called");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_coercible_property_reset_calls_attributeRemoved() {
|
||||||
|
const slider = new Slider();
|
||||||
|
slider.value = 10;
|
||||||
|
slider.ensureDomNode();
|
||||||
|
const domNode = slider.domNode;
|
||||||
|
|
||||||
|
let callbackCalled = false;
|
||||||
|
currentInspector.attributeRemoved = (nodeId: number, attrName: string) => {
|
||||||
|
assertEqual(nodeId, domNode.nodeId, "nodeId");
|
||||||
|
assertEqual(attrName, "value", "attrName");
|
||||||
|
callbackCalled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
slider.value = unsetValue;
|
||||||
|
|
||||||
|
assert(callbackCalled, "attributeRemoved not called");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_inspector_ui_setAttributeAsText_set_existing_property() {
|
||||||
|
// Arrange
|
||||||
|
const label = new Label();
|
||||||
|
|
||||||
|
label.text = "original label";
|
||||||
|
const expectedValue = "updated label";
|
||||||
|
|
||||||
|
label.ensureDomNode();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// simulate call from the inspector UI
|
||||||
|
currentInspector.setAttributeAsText(label.domNode.nodeId, "text='" + expectedValue + "'", "text");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertEqual(label.text, expectedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_inspector_ui_setAttributeAsText_remove_existing_property() {
|
||||||
|
// Arrange
|
||||||
|
const label = new Label();
|
||||||
|
label.text = "original label";
|
||||||
|
|
||||||
|
label.ensureDomNode();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// simulate call from the inspector UI
|
||||||
|
currentInspector.setAttributeAsText(label.domNode.nodeId, "" /* empty value - removes the attribute */, "text");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertEqual(label.text, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_inspector_ui_setAttributeAsText_set_new_property() {
|
||||||
|
// Arrange
|
||||||
|
const label = new Label();
|
||||||
|
const expectedValue = "custom";
|
||||||
|
|
||||||
|
label.ensureDomNode();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// simulate call from the inspector UI
|
||||||
|
currentInspector.setAttributeAsText(label.domNode.nodeId, "data-attr='" + expectedValue + "'" /* data-attr="custom" */, " " /* empty attr name initially */);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertEqual(label["data-attr"], expectedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_inspector_ui_removeNode() {
|
||||||
|
let childNodeRemovedCalled = false;
|
||||||
|
let stack = new StackLayout();
|
||||||
|
let label = new Label();
|
||||||
|
stack.addChild(label);
|
||||||
|
|
||||||
|
stack.ensureDomNode();
|
||||||
|
label.ensureDomNode();
|
||||||
|
|
||||||
|
let expectedParentId = stack.domNode.nodeId;
|
||||||
|
let expectedNodeId = label.domNode.nodeId;
|
||||||
|
|
||||||
|
currentInspector.childNodeRemoved = (parentId, nodeId) => {
|
||||||
|
childNodeRemovedCalled = true;
|
||||||
|
assertEqual(parentId, expectedParentId);
|
||||||
|
assertEqual(nodeId, expectedNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentInspector.removeNode(label.domNode.nodeId);
|
||||||
|
|
||||||
|
assert(childNodeRemovedCalled, "childNodeRemoved callback not called.");
|
||||||
|
}
|
@ -31,6 +31,9 @@ export function isRunningOnEmulator(): boolean {
|
|||||||
|
|
||||||
export var allTests = {};
|
export var allTests = {};
|
||||||
|
|
||||||
|
import * as domNodeTest from "./debugger/dom-node-tests";
|
||||||
|
allTests["DOM-NODE"] = domNodeTest;
|
||||||
|
|
||||||
import * as profilingTests from "./profiling/profiling-tests";
|
import * as profilingTests from "./profiling/profiling-tests";
|
||||||
allTests["PROFILING"] = profilingTests;
|
allTests["PROFILING"] = profilingTests;
|
||||||
|
|
||||||
|
81
tns-core-modules/debugger/css-agent.ts
Normal file
81
tns-core-modules/debugger/css-agent.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
On element select in the inspector the following are requested:
|
||||||
|
- Inline styles -> CSSStyle
|
||||||
|
- Attributes styles -> CSSStyle (Appears as 'Stacklayout[Attributes style]` - unsure of its purpose) irrelevant?
|
||||||
|
- Style matches -> RuleMatch[]
|
||||||
|
- Inherited styles -> InheritedStyleEntry[]
|
||||||
|
- Pseudo Element matches -> PseudoElementMatches[]
|
||||||
|
- Computed Styles for node -> CSSComputedStyleProperty[]
|
||||||
|
- Element Fonts -> PlatformFontUsage
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface CSSProperty {
|
||||||
|
name: string
|
||||||
|
value: string
|
||||||
|
disabled: boolean // strikes out the disabled property
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShorthandEntry { // seems irrelevant - feel free to leave empty for now
|
||||||
|
name: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CSSStyle {
|
||||||
|
cssProperties: CSSProperty[]
|
||||||
|
shorthandEntries: ShorthandEntry[] // doesn't seem to display anywhere
|
||||||
|
cssText?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Value {
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectorList { // e.g. [".btn", "Button", "Label"]
|
||||||
|
selectors: Value[]
|
||||||
|
text: string // doesn't seem to display anywhere
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CSSRule {
|
||||||
|
selectorList: SelectorList
|
||||||
|
origin: string // a constant - "regular"
|
||||||
|
style: CSSStyle
|
||||||
|
styleSheetId?: string // associated stylesheet
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RuleMatch {
|
||||||
|
rule: CSSRule
|
||||||
|
matchingSelectors: number[] // index-based - the index of the selector that the node currently inspected matches
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InheritedStyleEntry {
|
||||||
|
matchedCSSRules: RuleMatch[]
|
||||||
|
inlineStyle?: CSSStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CSSComputedStyleProperty {
|
||||||
|
name: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PlatformFontUsage {
|
||||||
|
familyName: string
|
||||||
|
glyphCount: number // number of characters in text of element
|
||||||
|
isCustomFont: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CSSStyleSheetHeader {
|
||||||
|
styleSheetId: string // a unique identifier - file name/path should do
|
||||||
|
frameId: string // constant
|
||||||
|
sourceUrl: string
|
||||||
|
origin: string // constant
|
||||||
|
title: string // the same as the id?
|
||||||
|
disabled: boolean // false - if the css has been invalidated/disabled
|
||||||
|
isInLine: boolean // false
|
||||||
|
startLine: number // constant - 1
|
||||||
|
startColumn: number // constant - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PseudoElementMatches {
|
||||||
|
pseudoType: string // e.g. last-child
|
||||||
|
matches: RuleMatch[]
|
||||||
|
}
|
97
tns-core-modules/debugger/devtools-elements.ts
Normal file
97
tns-core-modules/debugger/devtools-elements.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { unsetValue } from "../ui/core/properties";
|
||||||
|
import { ViewBase } from "../ui/core/view-base";
|
||||||
|
import { topmost } from "../ui/frame";
|
||||||
|
import { getNodeById } from "./dom-node";
|
||||||
|
|
||||||
|
export interface Inspector {
|
||||||
|
// DevTools -> Application communication. Methods that devtools calls when needed.
|
||||||
|
getDocument(): string;
|
||||||
|
removeNode(nodeId: number): void;
|
||||||
|
getComputedStylesForNode(nodeId: number): string;
|
||||||
|
setAttributeAsText(nodeId: number, text: string, name: string): void;
|
||||||
|
|
||||||
|
// Application -> DevTools communication. Methods that the app should call when needed.
|
||||||
|
childNodeInserted(parentId: number, lastId: number, nodeStr: string): void;
|
||||||
|
childNodeRemoved(parentId: number, nodeId: number): void;
|
||||||
|
attributeModified(nodeId: number, attrName: string, attrValue: string): void;
|
||||||
|
attributeRemoved(nodeId: number, attrName: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getViewById(nodeId: number): ViewBase {
|
||||||
|
const node = getNodeById(nodeId);
|
||||||
|
let view;
|
||||||
|
if (node) {
|
||||||
|
view = node.viewRef.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function attachInspectorCallbacks(inspector: Inspector) {
|
||||||
|
inspector.getDocument = function () {
|
||||||
|
const topMostFrame = topmost();
|
||||||
|
topMostFrame.ensureDomNode();
|
||||||
|
|
||||||
|
return topMostFrame.domNode.toJSON();
|
||||||
|
}
|
||||||
|
|
||||||
|
inspector.getComputedStylesForNode = function (nodeId) {
|
||||||
|
const view = getViewById(nodeId);
|
||||||
|
if (view) {
|
||||||
|
return JSON.stringify(view.domNode.getComputedProperties());
|
||||||
|
}
|
||||||
|
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
|
||||||
|
inspector.removeNode = function (nodeId) {
|
||||||
|
const view = getViewById(nodeId);
|
||||||
|
if (view) {
|
||||||
|
// Avoid importing layout and content view
|
||||||
|
let parent = <any>view.parent;
|
||||||
|
if (parent.removeChild) {
|
||||||
|
parent.removeChild(view);
|
||||||
|
} else if (parent.content === view) {
|
||||||
|
parent.content = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("Can't remove child from " + parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inspector.setAttributeAsText = function (nodeId, text, name) {
|
||||||
|
const view = getViewById(nodeId);
|
||||||
|
if (view) {
|
||||||
|
// attribute is registered for the view instance
|
||||||
|
let hasOriginalAttribute = !!name.trim();
|
||||||
|
|
||||||
|
if (text) {
|
||||||
|
let textParts = text.split("=");
|
||||||
|
|
||||||
|
if (textParts.length === 2) {
|
||||||
|
let attrName = textParts[0];
|
||||||
|
let attrValue = textParts[1].replace(/['"]+/g, '');
|
||||||
|
|
||||||
|
// if attr name is being replaced with another
|
||||||
|
if (name !== attrName && hasOriginalAttribute) {
|
||||||
|
view[name] = unsetValue;
|
||||||
|
view[attrName] = attrValue;
|
||||||
|
} else {
|
||||||
|
view[hasOriginalAttribute ? name : attrName] = attrValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// delete attribute
|
||||||
|
view[name] = unsetValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
view.domNode.loadAttributes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatically attach callbacks if there is __inspector
|
||||||
|
if (global && global.__inspector) {
|
||||||
|
attachInspectorCallbacks(global.__inspector)
|
||||||
|
}
|
197
tns-core-modules/debugger/dom-node.ts
Normal file
197
tns-core-modules/debugger/dom-node.ts
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
import { getSetProperties, getComputedCssValues } from "../ui/core/properties";
|
||||||
|
import { PercentLength } from "../ui/styling/style-properties";
|
||||||
|
import { ViewBase } from "../ui/core/view";
|
||||||
|
import { Color } from "../color";
|
||||||
|
import { CSSComputedStyleProperty } from "./css-agent";
|
||||||
|
import { Inspector } from "./devtools-elements";
|
||||||
|
|
||||||
|
const registeredDomNodes = {};
|
||||||
|
const ELEMENT_NODE_TYPE = 1;
|
||||||
|
const ROOT_NODE_TYPE = 9;
|
||||||
|
const propertyBlacklist = [
|
||||||
|
"effectivePaddingLeft",
|
||||||
|
"effectivePaddingBottom",
|
||||||
|
"effectivePaddingRight",
|
||||||
|
"effectivePaddingTop",
|
||||||
|
"effectiveBorderTopWidth",
|
||||||
|
"effectiveBorderRightWidth",
|
||||||
|
"effectiveBorderBottomWidth",
|
||||||
|
"effectiveBorderLeftWidth",
|
||||||
|
"effectiveMinWidth",
|
||||||
|
"nodeName",
|
||||||
|
"nodeType",
|
||||||
|
"decodeWidth",
|
||||||
|
"decodeHeight"
|
||||||
|
]
|
||||||
|
|
||||||
|
function notifyInspector(callback: (inspector: Inspector) => void) {
|
||||||
|
const ins = (<any>global).__inspector
|
||||||
|
if (ins) {
|
||||||
|
callback(ins);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function valueToString(value: any): string {
|
||||||
|
if (typeof value === "undefined" || value === null) {
|
||||||
|
return "";
|
||||||
|
} else if (value instanceof Color) {
|
||||||
|
return value.toString()
|
||||||
|
} else if (typeof value === "object") {
|
||||||
|
return PercentLength.convertToString(value)
|
||||||
|
} else {
|
||||||
|
return value + "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function propertyFilter([name, value]: [string, any]): boolean {
|
||||||
|
if (name[0] === "_") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value !== null && typeof value === "object") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertyBlacklist.indexOf(name) >= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerNode(domNode: DOMNode) {
|
||||||
|
registeredDomNodes[domNode.nodeId] = domNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unregisterNode(domNode: DOMNode) {
|
||||||
|
delete registeredDomNodes[domNode.nodeId];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNodeById(id: number): DOMNode {
|
||||||
|
return registeredDomNodes[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DOMNode {
|
||||||
|
nodeId;
|
||||||
|
nodeType;
|
||||||
|
nodeName;
|
||||||
|
localName;
|
||||||
|
nodeValue = '';
|
||||||
|
attributes: string[] = [];
|
||||||
|
viewRef: WeakRef<ViewBase>;
|
||||||
|
|
||||||
|
constructor(view: ViewBase) {
|
||||||
|
this.viewRef = new WeakRef(view);
|
||||||
|
this.nodeType = view.typeName === "Frame" ? ROOT_NODE_TYPE : ELEMENT_NODE_TYPE;
|
||||||
|
this.nodeId = view._domId;
|
||||||
|
this.nodeName = view.typeName;
|
||||||
|
this.localName = this.nodeName;
|
||||||
|
|
||||||
|
// Load all attributes
|
||||||
|
this.loadAttributes();
|
||||||
|
|
||||||
|
registerNode(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public loadAttributes() {
|
||||||
|
this.attributes = [];
|
||||||
|
getSetProperties(this.viewRef.get())
|
||||||
|
.filter(propertyFilter)
|
||||||
|
.forEach(pair => this.attributes.push(pair[0], pair[1] + ""));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
get children(): DOMNode[] {
|
||||||
|
const view = this.viewRef.get();
|
||||||
|
if (!view) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = [];
|
||||||
|
|
||||||
|
view.eachChild((child) => {
|
||||||
|
child.ensureDomNode();
|
||||||
|
res.push(child.domNode);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
onChildAdded(childView: ViewBase): void {
|
||||||
|
notifyInspector((ins) => {
|
||||||
|
const view = this.viewRef.get();
|
||||||
|
childView.ensureDomNode();
|
||||||
|
|
||||||
|
let previousChild: ViewBase;
|
||||||
|
view.eachChild((child) => {
|
||||||
|
if (child === childView) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
previousChild = child;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
const index = !!previousChild ? previousChild._domId : 0;
|
||||||
|
|
||||||
|
ins.childNodeInserted(this.nodeId, index, childView.domNode.toJSON());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onChildRemoved(view: ViewBase): void {
|
||||||
|
notifyInspector((ins) => {
|
||||||
|
ins.childNodeRemoved(this.nodeId, view.domNode.nodeId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeModified(name: string, value: any) {
|
||||||
|
notifyInspector((ins) => {
|
||||||
|
ins.attributeModified(this.nodeId, name, valueToString(value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeRemoved(name: string) {
|
||||||
|
notifyInspector((ins) => {
|
||||||
|
ins.attributeRemoved(this.nodeId, name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getComputedProperties(): CSSComputedStyleProperty[] {
|
||||||
|
const view = this.viewRef.get();
|
||||||
|
if (!view) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = getComputedCssValues(view)
|
||||||
|
.filter(pair => pair[0][0] !== "_")
|
||||||
|
.map((pair) => {
|
||||||
|
return {
|
||||||
|
name: pair[0],
|
||||||
|
value: valueToString(pair[1])
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
unregisterNode(this);
|
||||||
|
this.viewRef.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON() {
|
||||||
|
return JSON.stringify(this.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
private toObject() {
|
||||||
|
return {
|
||||||
|
nodeId: this.nodeId,
|
||||||
|
nodeType: this.nodeType,
|
||||||
|
nodeName: this.nodeName,
|
||||||
|
localName: this.localName,
|
||||||
|
nodeValue: this.nodeValue,
|
||||||
|
children: this.children.map(c => c.toObject()),
|
||||||
|
attributes: this.attributes
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
@ -122,3 +122,6 @@ export function clearInheritedProperties(view: ViewBase): void;
|
|||||||
|
|
||||||
export function makeValidator<T>(...values: T[]): (value: any) => value is T;
|
export function makeValidator<T>(...values: T[]): (value: any) => value is T;
|
||||||
export function makeParser<T>(isValid: (value: any) => boolean): (value: any) => T;
|
export function makeParser<T>(isValid: (value: any) => boolean): (value: any) => T;
|
||||||
|
|
||||||
|
export function getSetProperties(view: ViewBase): [string, any][];
|
||||||
|
export function getComputedCssValues(view: ViewBase): [string, any][];
|
@ -12,6 +12,7 @@ export { Style };
|
|||||||
|
|
||||||
export const unsetValue: any = new Object();
|
export const unsetValue: any = new Object();
|
||||||
|
|
||||||
|
let cssPropertyNames: string[] = [];
|
||||||
let symbolPropertyMap = {};
|
let symbolPropertyMap = {};
|
||||||
let cssSymbolPropertyMap = {};
|
let cssSymbolPropertyMap = {};
|
||||||
|
|
||||||
@ -152,6 +153,14 @@ export class Property<T extends ViewBase, U> implements TypedPropertyDescriptor<
|
|||||||
if (affectsLayout) {
|
if (affectsLayout) {
|
||||||
this.requestLayout();
|
this.requestLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.domNode) {
|
||||||
|
if (reset) {
|
||||||
|
this.domNode.attributeRemoved(propertyName);
|
||||||
|
} else {
|
||||||
|
this.domNode.attributeModified(propertyName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -179,6 +188,10 @@ export class Property<T extends ViewBase, U> implements TypedPropertyDescriptor<
|
|||||||
if (affectsLayout) {
|
if (affectsLayout) {
|
||||||
owner.requestLayout();
|
owner.requestLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (owner.domNode) {
|
||||||
|
owner.domNode.attributeModified(propertyName, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -299,6 +312,14 @@ export class CoercibleProperty<T extends ViewBase, U> extends Property<T, U> imp
|
|||||||
if (affectsLayout) {
|
if (affectsLayout) {
|
||||||
this.requestLayout();
|
this.requestLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.domNode) {
|
||||||
|
if (reset) {
|
||||||
|
this.domNode.attributeRemoved(propertyName);
|
||||||
|
} else {
|
||||||
|
this.domNode.attributeModified(propertyName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -399,6 +420,8 @@ export class CssProperty<T extends Style, U> implements definitions.CssProperty<
|
|||||||
const propertyName = options.name;
|
const propertyName = options.name;
|
||||||
this.name = propertyName;
|
this.name = propertyName;
|
||||||
|
|
||||||
|
cssPropertyNames.push(options.cssName);
|
||||||
|
|
||||||
this.cssName = `css:${options.cssName}`;
|
this.cssName = `css:${options.cssName}`;
|
||||||
this.cssLocalName = options.cssName;
|
this.cssLocalName = options.cssName;
|
||||||
|
|
||||||
@ -635,6 +658,8 @@ export class CssAnimationProperty<T extends Style, U> {
|
|||||||
const propertyName = options.name;
|
const propertyName = options.name;
|
||||||
this.name = propertyName;
|
this.name = propertyName;
|
||||||
|
|
||||||
|
cssPropertyNames.push(options.cssName);
|
||||||
|
|
||||||
CssAnimationProperty.properties[propertyName] = this;
|
CssAnimationProperty.properties[propertyName] = this;
|
||||||
if (options.cssName && options.cssName !== propertyName) {
|
if (options.cssName && options.cssName !== propertyName) {
|
||||||
CssAnimationProperty.properties[options.cssName] = this;
|
CssAnimationProperty.properties[options.cssName] = this;
|
||||||
@ -1207,3 +1232,33 @@ export function makeParser<T>(isValid: (value: any) => boolean): (value: any) =>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSetProperties(view: ViewBase): [string, any][] {
|
||||||
|
const result = [];
|
||||||
|
|
||||||
|
Object.getOwnPropertyNames(view).forEach(prop => {
|
||||||
|
result.push([prop, view[prop]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
let symbols = Object.getOwnPropertySymbols(view);
|
||||||
|
for (let symbol of symbols) {
|
||||||
|
const property = symbolPropertyMap[symbol];
|
||||||
|
if (!property) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = view[property.key];
|
||||||
|
result.push([property.name, value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getComputedCssValues(view: ViewBase): [string, any][] {
|
||||||
|
const result = [];
|
||||||
|
const style = view.style;
|
||||||
|
for (var prop of cssPropertyNames) {
|
||||||
|
result.push([prop, style[prop]]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
@ -15,6 +15,7 @@ import { layout } from "../../../utils/utils";
|
|||||||
import { Color } from "../../../color";
|
import { Color } from "../../../color";
|
||||||
import { Order, FlexGrow, FlexShrink, FlexWrapBefore, AlignSelf } from "../../layouts/flexbox-layout";
|
import { Order, FlexGrow, FlexShrink, FlexWrapBefore, AlignSelf } from "../../layouts/flexbox-layout";
|
||||||
import { Length } from "../../styling/style-properties";
|
import { Length } from "../../styling/style-properties";
|
||||||
|
import { DOMNode } from "../../../debugger/dom-node";
|
||||||
|
|
||||||
export { isIOS, isAndroid, layout, Color };
|
export { isIOS, isAndroid, layout, Color };
|
||||||
|
|
||||||
@ -57,6 +58,7 @@ export abstract class ViewBase extends Observable {
|
|||||||
col: number;
|
col: number;
|
||||||
rowSpan: number;
|
rowSpan: number;
|
||||||
colSpan: number;
|
colSpan: number;
|
||||||
|
domNode: DOMNode;
|
||||||
|
|
||||||
order: Order;
|
order: Order;
|
||||||
flexGrow: FlexGrow;
|
flexGrow: FlexGrow;
|
||||||
@ -277,7 +279,7 @@ export abstract class ViewBase extends Observable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the core logic of adding a child view to the native visual tree. Returns true if the view's native representation has been successfully added, false otherwise.
|
* Performs the core logic of adding a child view to the native visual tree. Returns true if the view's native representation has been successfully added, false otherwise.
|
||||||
*/
|
*/
|
||||||
_addViewToNativeVisualTree(view: ViewBase, atIndex?: number): boolean;
|
_addViewToNativeVisualTree(view: ViewBase, atIndex?: number): boolean;
|
||||||
_removeViewFromNativeVisualTree(view: ViewBase): void;
|
_removeViewFromNativeVisualTree(view: ViewBase): void;
|
||||||
_childIndexToNativeChildIndex(index?: number): number;
|
_childIndexToNativeChildIndex(index?: number): number;
|
||||||
@ -296,6 +298,12 @@ export abstract class ViewBase extends Observable {
|
|||||||
*/
|
*/
|
||||||
public deletePseudoClass(name: string): void;
|
public deletePseudoClass(name: string): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @unstable
|
||||||
|
* Ensures a dom-node for this view.
|
||||||
|
*/
|
||||||
|
public ensureDomNode();
|
||||||
|
|
||||||
//@private
|
//@private
|
||||||
public _styleScope: any;
|
public _styleScope: any;
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import { Binding, BindingOptions, Observable, WrappedValue, PropertyChangeData,
|
|||||||
import { isIOS, isAndroid } from "../../../platform";
|
import { isIOS, isAndroid } from "../../../platform";
|
||||||
import { layout } from "../../../utils/utils";
|
import { layout } from "../../../utils/utils";
|
||||||
import { Length, paddingTopProperty, paddingRightProperty, paddingBottomProperty, paddingLeftProperty } from "../../styling/style-properties";
|
import { Length, paddingTopProperty, paddingRightProperty, paddingBottomProperty, paddingLeftProperty } from "../../styling/style-properties";
|
||||||
|
import { DOMNode } from "../../../debugger/dom-node";
|
||||||
|
|
||||||
// TODO: Remove this import!
|
// TODO: Remove this import!
|
||||||
import * as types from "../../../utils/types";
|
import * as types from "../../../utils/types";
|
||||||
@ -142,6 +143,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
|||||||
private _visualState: string;
|
private _visualState: string;
|
||||||
private _inlineStyleSelector: SelectorCore;
|
private _inlineStyleSelector: SelectorCore;
|
||||||
private __nativeView: any;
|
private __nativeView: any;
|
||||||
|
public domNode: DOMNode;
|
||||||
|
|
||||||
public bindingContext: any;
|
public bindingContext: any;
|
||||||
public nativeView: any;
|
public nativeView: any;
|
||||||
@ -259,7 +261,13 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overriden so we don't raise `poropertyChange`
|
public ensureDomNode() {
|
||||||
|
if (!this.domNode) {
|
||||||
|
this.domNode = new DOMNode(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overridden so we don't raise `poropertyChange`
|
||||||
// The property will raise its own event.
|
// The property will raise its own event.
|
||||||
public set(name: string, value: any) {
|
public set(name: string, value: any) {
|
||||||
this[name] = WrappedValue.unwrap(value);
|
this[name] = WrappedValue.unwrap(value);
|
||||||
@ -558,6 +566,10 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
|||||||
view.parent = this;
|
view.parent = this;
|
||||||
this._addViewCore(view, atIndex);
|
this._addViewCore(view, atIndex);
|
||||||
view._parentChanged(null);
|
view._parentChanged(null);
|
||||||
|
|
||||||
|
if (this.domNode) {
|
||||||
|
this.domNode.onChildAdded(view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@profile
|
@profile
|
||||||
@ -590,7 +602,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core logic for removing a child view from this instance. Used by the framework to handle lifecycle events more centralized. Do not outside the UI Stack implementation.
|
* Core logic for removing a child view from this instance. Used by the framework to handle lifecycle events more centralized. Do not use outside the UI Stack implementation.
|
||||||
*/
|
*/
|
||||||
public _removeView(view: ViewBase) {
|
public _removeView(view: ViewBase) {
|
||||||
if (traceEnabled()) {
|
if (traceEnabled()) {
|
||||||
@ -601,6 +613,10 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
|||||||
throw new Error("View not added to this instance. View: " + view + " CurrentParent: " + view.parent + " ExpectedParent: " + this);
|
throw new Error("View not added to this instance. View: " + view + " CurrentParent: " + view.parent + " ExpectedParent: " + this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.domNode) {
|
||||||
|
this.domNode.onChildRemoved(view);
|
||||||
|
}
|
||||||
|
|
||||||
this._removeViewCore(view);
|
this._removeViewCore(view);
|
||||||
view.parent = undefined;
|
view.parent = undefined;
|
||||||
view._parentChanged(this);
|
view._parentChanged(this);
|
||||||
@ -785,6 +801,12 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
|||||||
// this._iosView = null;
|
// this._iosView = null;
|
||||||
|
|
||||||
this._context = null;
|
this._context = null;
|
||||||
|
|
||||||
|
if (this.domNode) {
|
||||||
|
this.domNode.dispose();
|
||||||
|
this.domNode = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
traceNotifyEvent(this, "_onContextChanged");
|
traceNotifyEvent(this, "_onContextChanged");
|
||||||
traceNotifyEvent(this, "_tearDownUI");
|
traceNotifyEvent(this, "_tearDownUI");
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,10 @@ function onLivesync(args: EventData): void {
|
|||||||
}
|
}
|
||||||
application.on("livesync", onLivesync);
|
application.on("livesync", onLivesync);
|
||||||
|
|
||||||
|
if (global && global.__inspector) {
|
||||||
|
require("tns-core-modules/debugger/devtools-elements");
|
||||||
|
}
|
||||||
|
|
||||||
let frameStack: Array<FrameBase> = [];
|
let frameStack: Array<FrameBase> = [];
|
||||||
|
|
||||||
function buildEntryFromArgs(arg: any): NavigationEntry {
|
function buildEntryFromArgs(arg: any): NavigationEntry {
|
||||||
|
4
tns-core-modules/ui/slider/slider.d.ts
vendored
4
tns-core-modules/ui/slider/slider.d.ts
vendored
@ -38,12 +38,12 @@ export class Slider extends View {
|
|||||||
/**
|
/**
|
||||||
* Represents the observable property backing the value property of each Slider instance.
|
* Represents the observable property backing the value property of each Slider instance.
|
||||||
*/
|
*/
|
||||||
export const valueProperty: Property<Slider, number>;
|
export const valueProperty: CoercibleProperty<Slider, number>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the observable property backing the minValue property of each Slider instance.
|
* Represents the observable property backing the minValue property of each Slider instance.
|
||||||
*/
|
*/
|
||||||
export const minValueProperty: CoercibleProperty<Slider, number>;
|
export const minValueProperty: Property<Slider, number>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the observable property backing the maxValue property of each Slider instance.
|
* Represents the observable property backing the maxValue property of each Slider instance.
|
||||||
|
@ -129,7 +129,7 @@ export class TextBase extends TextBaseCommon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overriden in TextField becasue setSingleLine(false) will remove methodTransformation.
|
// Overridden in TextField because setSingleLine(false) will remove methodTransformation.
|
||||||
// and we don't want to allow TextField to be multiline
|
// and we don't want to allow TextField to be multiline
|
||||||
[whiteSpaceProperty.setNative](value: WhiteSpace) {
|
[whiteSpaceProperty.setNative](value: WhiteSpace) {
|
||||||
const nativeView = this.nativeView;
|
const nativeView = this.nativeView;
|
||||||
|
Reference in New Issue
Block a user