提交 b15263b4 编写于 作者: A Adam Barth

Clean up examples directory

This CL removes a bunch of examples that don't actually work in the current
engine. I've also renamed example-element to custom-element and
example-scrollable to scrolling because the word "example" in the name is
redundant with the name of the directory.

R=eseidel@chromium.org

Review URL: https://codereview.chromium.org/980323003
上级 383ea842
Some of these examples are examples of what sky supports now.
Others are examples of what sky hopes to one day support.
Therefore not all these examples actually work today.
<!--
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-->
<import src="../data/cities.sky" as="cities" />
<script>
function CityDataService(cities) {
this.cities = cities;
// sort by state, city name.
this.cities.sort(function(a, b) {
if (a.state != b.state) {
return a.state < b.state ? -1 : 1;
}
return a.name < b.name ? -1 : 1;
});
}
CityDataService.prototype.get = function(index, count) {
var self = this;
return new Promise(function(fulfill) {
var result = [];
while (count-- > 0) {
while (index < 0) {
index += self.cities.length;
}
if (index >= self.cities.length)
index = index % self.cities.length;
result.push(self.cities[index]);
index++;
}
fulfill(result);
});
}
module.exports = {
service: new Promise(function(fulfill) {
fulfill(new CityDataService(cities));
})
};
</script>
<!--
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-->
<import src="../../framework/sky-element/sky-element.sky" as="SkyElement" />
<import src="city-data-service.sky" as="CityDataService" />
<import src="city-sequence.sky" as="CitySequence" />
<sky-element name="state-header">
<template>
<style>
div {
font-size: 16px;
color: #FFF;
background-color: #333;
padding: 4px 4px 4px 12px;
display: paragraph;
}
</style>
<div>{{ state }}</div>
</template>
<script>
module.exports.StateHeaderElement = class extends SkyElement {
created() {
this.state = "";
}
set datum(datum) {
this.state = datum.state;
}
}.register();
</script>
</sky-element>
<sky-element name="letter-header">
<template>
<style>
div {
font-size: 12px;
font-weight: bold;
padding: 2px 4px 4px 12px;
background-color: #DDD;
display: paragraph;
}
</style>
<div>{{ letter }}</div>
</template>
<script>
module.exports.LetterHeaderElement = class extends SkyElement {
created() {
this.letter = "";
}
set datum(datum) {
this.letter = datum.letter;
}
}.register();
</script>
</sky-element>
<sky-element name="city-item">
<template>
<style>
:host {
display: flex;
font-size: 13px;
padding: 8px 4px 4px 12px;
border-bottom: 1px solid #EEE;
line-height: 15px;
overflow: hidden;
}
div {
display: paragraph;
}
span {
display: inline;
}
#name {
font-weight: bold
}
#population {
color: #AAA;
}
</style>
<div>
<span id="name">{{ name }}</span>
<t>, </t>
<span id="population">population {{ population }}</span>
</div>
</template>
<script>
module.exports.CityItemElement = class extends SkyElement {
created() {
this.name = "";
this.population = "";
}
set datum(datum) {
this.name = datum.name;
this.population = datum.population;
}
}.register();
</script>
</sky-element>
<sky-element name="city-list">
<template>
<style>
:host {
overflow: hidden;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: block;
background-color: #fff;
}
#contentarea {
will-change: transform;
}
.position {
position: absolute;
left: 0;
right: 0;
}
</style>
<div id="contentarea">
</div>
</template>
<script>
(function(global) {
"use strict";
var LOAD_LENGTH = 20;
var LOAD_BUFFER_PRE = LOAD_LENGTH * 4;
var LOAD_BUFFER_POST = LOAD_LENGTH * 4;
function Loader(cityList) {
this.cityList = cityList;
this.loadingData = false;
this.data = null;
this.zeroIndex = 0;
this.loadIndex = 0;
}
Loader.prototype.localIndex = function(externalIndex) {
return externalIndex + this.zeroIndex;
}
Loader.prototype.externalIndex = function(localIndex) {
return localIndex - this.zeroIndex;
}
Loader.prototype.getItems = function() {
return this.data ? this.data.items : [];
}
Loader.prototype.maybeLoadMoreData =
function(dataloadedCallback, firstVisible) {
if (this.loadingData)
return;
if (firstVisible) {
this.loadIndex = this.externalIndex(
this.data.items.indexOf(firstVisible));
}
var localIndex = this.localIndex(this.loadIndex);
var loadedPre = 0;
var loadedPost = 0;
if (this.data) {
loadedPre = localIndex;
loadedPost = this.data.items.length - loadedPre;
}
var loadTime;
if (loadedPre >= LOAD_BUFFER_PRE &&
loadedPost >= LOAD_BUFFER_POST) {
var cityList = this.cityList;
setTimeout(function() {
cityList.dispatchEvent(new Event('load'));
});
if (window.startLoad) {
loadTime = new Date().getTime() - window.startLoad;
console.log('Load: ' + loadTime + 'ms');
window.startLoad = undefined;
}
return;
}
this.loadingData = true;
var loadIndex;
if (!this.data) {
// Initial batch
loadIndex = 0;
} else if (loadedPost < LOAD_BUFFER_POST) {
// Load forward first
loadIndex = this.data.items.length;
} else {
// Then load backward
loadIndex = -LOAD_LENGTH;
}
var self = this;
var externalIndex = this.externalIndex(loadIndex);
try {
CityDataService.service.then(function(cityService) {
return cityService.get(externalIndex, LOAD_LENGTH)
.then(function(cities) {
var indexOffset = 0;
var newData = new CitySequence(cities);
if (!self.data) {
self.data = newData;
} else if (loadIndex > 0) {
self.data.append(newData);
} else {
self.zeroIndex += LOAD_LENGTH;
indexOffset = LOAD_LENGTH;
newData.append(self.data);
self.data = newData;
}
self.loadingData = false;
dataloadedCallback(self.data, indexOffset);
});
}).catch(function(ex) {
console.log(ex.stack);
});
} catch (ex) {
console.log(ex.stack);
}
}
function Scroller() {
this.contentarea = null;
this.scrollTop = 0;
this.scrollHeight = -1;
}
Scroller.prototype.setup = function(scrollHeight, contentarea) {
this.scrollHeight = scrollHeight;
this.contentarea = contentarea;
}
Scroller.prototype.scrollBy = function(amount) {
this.scrollTop += amount;
this.scrollTo();
}
Scroller.prototype.scrollTo = function() {
var transform = 'translateY(' + -this.scrollTop.toFixed(2) + 'px)';
this.contentarea.style.transform = transform;
}
// Current position and height of the scroller, that could
// be used (by Tiler, for example) to reason about where to
// place visible things.
Scroller.prototype.getCurrentFrame = function() {
return { top: this.scrollTop, height: this.scrollHeight };
}
Scroller.prototype.hasFrame = function() {
return this.scrollHeight != -1;
}
function Tile(datum, element, viewType, index) {
this.datum = datum;
this.element = element;
this.viewType = viewType;
this.index = index;
}
function Tiler(contentArea, views, viewHeights) {
this.contentArea = contentArea;
this.drawTop = 0;
this.drawBottom = 0;
this.firstItem = -1;
this.tiles = [];
this.viewHeights = viewHeights;
this.views = views;
}
Tiler.prototype.setupViews = function(scrollFrame) {
for (var type in this.viewHeights) {
this.initializeViewType(scrollFrame, type, this.viewHeights[type]);
}
}
Tiler.prototype.initializeViewType = function(scrollFrame, viewType,
height) {
var count = Math.ceil(scrollFrame.height / height) * 2;
var viewCache = this.views[viewType] = {
indices: [],
elements: []
};
var protoElement;
switch (viewType) {
case 'stateHeader':
protoElement = document.createElement('state-header');
break;
case 'letterHeader':
protoElement = document.createElement('letter-header');
break;
case 'cityItem':
protoElement = document.createElement('city-item');
break;
default:
console.warn('Unknown viewType: ' + viewType);
}
protoElement.style.display = 'none';
protoElement.style.height = height;
protoElement.classList.add('position');
for (var i = 0; i < count; i++) {
var clone = protoElement.cloneNode(false);
this.contentArea.appendChild(clone);
viewCache.elements.push(clone);
viewCache.indices.push(i);
}
}
Tiler.prototype.checkoutTile = function(viewType, datum, top) {
var viewCache = this.views[viewType];
var index = viewCache.indices.pop();
var element = viewCache.elements[index];
element.datum = datum;
element.style.display = '';
element.style.top = top + 'px';
return new Tile(datum, element, viewType, index);
}
Tiler.prototype.checkinTile = function(tile) {
if (!tile.element)
return;
tile.element.style.display = 'none';
this.views[tile.viewType].indices.push(tile.index);
}
Tiler.prototype.getFirstVisibleDatum = function(scrollFrame) {
var tiles = this.tiles;
var viewHeights = this.viewHeights;
var itemTop = this.drawTop - scrollFrame.top;
if (itemTop >= 0 && tiles.length)
return tiles[0].datum;
var tile;
for (var i = 0; i < tiles.length && itemTop < 0; i++) {
tile = tiles[i];
var height = viewHeights[tile.viewType];
itemTop += height;
}
return tile ? tile.datum : null;
}
Tiler.prototype.viewType = function(datum) {
switch (datum.headerOrder) {
case 1: return 'stateHeader';
case 2: return 'letterHeader';
default: return 'cityItem';
}
}
Tiler.prototype.drawTiles = function(scrollFrame, data) {
var tiles = this.tiles;
var viewHeights = this.viewHeights;
var buffer = Math.round(scrollFrame.height / 2);
var targetTop = scrollFrame.top - buffer;
var targetBottom = scrollFrame.top + scrollFrame.height + buffer;
// Collect down to targetTop
var first = tiles[0];
while (tiles.length &&
targetTop > this.drawTop + viewHeights[first.viewType]) {
var height = viewHeights[first.viewType];
this.drawTop += height;
this.firstItem++;
this.checkinTile(tiles.shift());
first = tiles[0];
}
// Collect up to targetBottom
var last = tiles[tiles.length - 1];
while(tiles.length &&
targetBottom < this.drawBottom - viewHeights[last.viewType]) {
var height = viewHeights[last.viewType];
this.drawBottom -= height;
this.checkinTile(tiles.pop());
last = tiles[tiles.length - 1];
}
// Layout up to targetTop
while (this.firstItem > 0 &&
targetTop < this.drawTop) {
var datum = data[this.firstItem - 1];
var type = this.viewType(datum);
var height = viewHeights[type];
this.drawTop -= height;
var tile = targetBottom < this.drawTop ?
new Tile(datum, null, datum.viewType, -1) : // off-screen
this.checkoutTile(type, datum, this.drawTop);
this.firstItem--;
tiles.unshift(tile);
}
// Layout down to targetBottom
while (this.firstItem + tiles.length < data.length - 1 &&
targetBottom > this.drawBottom) {
var datum = data[this.firstItem + tiles.length];
var type = this.viewType(datum);
var height = viewHeights[type];
this.drawBottom += height;
var tile = targetTop > this.drawBottom ?
new Tile(datum, null, datum.viewType, -1) : // off-screen
this.checkoutTile(type, datum, this.drawBottom - height);
tiles.push(tile);
}
// Debug validate:
// for (var i = 0; i < tiles.length; i++) {
// if (tiles[i].datum !== data[this.firstItem + i])
// throw Error('Invalid')
// }
}
// FIXME: Needs better name.
Tiler.prototype.updateFirstItem = function(offset) {
var tiles = this.tiles;
if (!tiles.length) {
this.firstItem = 0;
} else {
this.firstItem += offset;
}
}
module.exports.CityListElement = class extends SkyElement {
created() {
this.loader = null;
this.scroller = null;
this.tiler = null;
this.date = null;
this.month = null;
this.views = null;
}
attached() {
this.views = {};
this.loader = new Loader(this);
this.scroller = new Scroller();
this.tiler = new Tiler(
this.shadowRoot.getElementById('contentarea'), this.views, {
stateHeader: 27,
letterHeader: 18,
cityItem: 30
});
var self = this;
this.addEventListener('wheel', function(event) {
self.scrollBy(-event.offsetY)
});
setTimeout(function() {
self.domReady();
self.loader.maybeLoadMoreData(self.dataLoaded.bind(self));
});
}
domReady() {
this.scroller.setup(this.clientHeight,
this.shadowRoot.getElementById('contentarea'));
var scrollFrame = this.scroller.getCurrentFrame();
this.tiler.setupViews(scrollFrame);
}
updateView(data, scrollChanged) {
var scrollFrame = this.scroller.getCurrentFrame();
this.tiler.drawTiles(scrollFrame, data);
var datum = scrollChanged ?
this.tiler.getFirstVisibleDatum(scrollFrame) : null;
this.loader.maybeLoadMoreData(this.dataLoaded.bind(this), datum);
}
dataLoaded(data, indexOffset) {
var scrollFrame = this.scroller.getCurrentFrame();
this.tiler.updateFirstItem(indexOffset);
this.updateView(data.items, false);
}
scrollBy(amount) {
this.scroller.scrollBy(amount);
this.updateView(this.loader.getItems(), true);
}
}.register();
})(this);
</script>
</sky-element>
<!--
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-->
<script>
function StateHeader(state) {
this.state = state;
this.headerOrder = 1;
};
function LetterHeader(letter) {
this.letter = letter;
this.headerOrder = 2;
}
function CitySequence(cities)
{
this.items = [];
this.cursor = 0;
var lastState;
var lastLetter;
for (var i = 0; i < cities.length; i++) {
var city = cities[i];
if (!lastState || lastState.state != city.state) {
lastState = new StateHeader(city.state);
this.items.push(lastState);
lastLetter = undefined;
}
if (!lastLetter || lastLetter.letter != city.name[0]) {
lastLetter = new LetterHeader(city.name[0]);
this.items.push(lastLetter);
}
this.items.push(city);
}
};
CitySequence.prototype = {
append: function(other) {
var lastCity = this.items[this.items.length - 1];
var firstOtherCity = other.items[2];
var index = 0;
if (firstOtherCity.state == lastCity.state) {
// skip StateHeader
if (firstOtherCity.name[0] == lastCity.name[0]) {
// skip LetterHeader
index = 2;
} else {
index = 1;
}
}
for (; index < other.items.length; index++) {
this.items.push(other.items[index]);
}
}
};
module.exports = CitySequence;
</script>
<!--
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport"
content="initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=yes, width=device-width">
<import src="city-list.sky" />
<style>
html, body {
margin: 0;
padding: 0;
font-family: "Roboto", "HelveticaNeue",sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 13px;
color: #222;
width: 100%;
height: 100%;
-webkit-user-select: none;
}
</style>
</head>
<script>
window.startLoad = new Date().getTime();
</script>
<body>
<city-list></city-list>
<script>
var cityList = document.querySelector('city-list');
var scrollBy = 0;
var toks = location.search.match(/auto=([0-9]+)/);
if (toks) {
scrollBy = Number(toks[1]);
}
function autoScroll() {
cityList.scrollBy(scrollBy);
requestAnimationFrame(autoScroll);
}
if (scrollBy) {
setTimeout(function() {
requestAnimationFrame(autoScroll);
}, 200)
}
</script>
</body>
</html>
#!mojo mojo:sky_viewer
<sky>
<import src="dart-library.sky" as="library" />
<script>
import 'dart:async';
import '/mojo/public/dart/application.dart';
import 'dart:typed_data';
import 'dart:sky.internals' as internals;
import 'mojo:bindings';
import 'mojo:core';
import 'package:mojo/services/network/public/interfaces/network_service.mojom.dart';
import 'package:mojo/services/network/public/interfaces/url_loader.mojom.dart';
import 'package:mojo/public/interfaces/application/shell.mojom.dart' as shell_mojom;
class WGet extends Application {
NetworkServiceProxy _networkService;
UrlLoaderProxy _urlLoaderProxy;
WGet.fromHandle(MojoHandle handle) : super.fromHandle(handle);
WGet(MojoMessagePipeEndpoint endpoint) : super(endpoint);
void initialize(List<String> args, String url) {
run(args);
}
run(List<String> args) async {
if (args == null || args.length != 2) {
throw "Expected URL argument";
}
ByteData bodyData = await _getUrl(args[1]);
print("read ${bodyData.lengthInBytes} bytes");
_closeProxies();
close();
}
Future<ByteData> _getUrl(String url) async {
_initProxiesIfNeeded();
var urlRequest = new UrlRequest()
..url = url
..autoFollowRedirects = true;
var urlResponse = await _urlLoaderProxy.start(urlRequest);
print("url => ${urlResponse.response.url}");
print("status_line => ${urlResponse.response.statusLine}");
print("mime_type => ${urlResponse.response.mimeType}");
return DataPipeDrainer.drainHandle(urlResponse.response.body);
}
void _initProxiesIfNeeded() {
if (_networkService == null) {
_networkService = new NetworkServiceProxy.unbound();
connectToService("mojo:network_service", _networkService);
}
if (_urlLoaderProxy == null) {
_urlLoaderProxy = new UrlLoaderProxy.unbound();
_networkService.createUrlLoader(_urlLoaderProxy);
}
}
void _closeProxies() {
_urlLoaderProxy.close();
_networkService.close();
_urlLoaderProxy = null;
_networkService = null;
}
}
main() {
var messagePipe = new MojoMessagePipe();
var wget = new WGet(messagePipe.endpoints[1]);
wget.listen();
var shellProxy = new shell_mojom.ShellProxy.fromHandle(new MojoHandle(internals.takeShellProxyHandle()));
wget.initializeFromShellProxy(shellProxy, ["mojo:wget", "http://www.google.com"], "mojo:wget");
}
</script>
</sky>
<import src="../../framework/sky-element/sky-element.sky" as="SkyElement" />
<sky-element name="app-header">
<template>
<style>
:host {
display: flex;
background: linear-gradient(#3E77B7, #6C98C4);
padding: 8px;
color: white;
border-bottom: 1px solid #3E77B7;
}
</style>
<content></content>
</template>
<script>
module.exports = class extends SkyElement {
}.register();
</script>
</sky-element>
<import src="../../framework/sky-element/sky-element.sky" as="SkyElement" />
<sky-element name="app-menu-button">
<template>
<style>
:host {
display: flex;
border-radius: 4px;
justify-content: center;
align-items: center;
width: 30px;
height: 30px;
}
</style>
<content></content>
</template>
<script>
module.exports = class extends SkyElement {
}.register();
</script>
</sky-element>
<import src="../../framework/sky-element/sky-element.sky" as="SkyElement" />
<sky-element name="app-panel-content">
<template>
<style>
:host {
display: flex;
}
</style>
<content></content>
</template>
<script>
module.exports = class extends SkyElement {
}.register();
</script>
</sky-element>
<import src="../../framework/sky-element/sky-element.sky" as="SkyElement" />
<sky-element name="app-panel-header">
<template>
<style>
:host {
background-color: #DEDEDE;
padding: 8px;
display: flex;
align-items: center;
}
</style>
<content></content>
</template>
<script>
module.exports = class extends SkyElement {
}.register();
</script>
</sky-element>
<import src="../../framework/sky-element/sky-element.sky" as="SkyElement" />
<sky-element name="app-panel">
<template>
<style>
:host {
display: flex;
flex-direction: column;
background-color: white;
}
</style>
<content></content>
</template>
<script>
module.exports = class extends SkyElement {
}.register();
</script>
</sky-element>
<import src="../../framework/sky-element/sky-element.sky" as="SkyElement" />
<sky-element name="app-scrollable">
<template>
<style>
:host {
flex: 1;
box-shadow: inset 0px 0px 22px 2px rgba(22, 22, 22, 0.63);
overflow: hidden;
}
</style>
<content></content>
</template>
<script>
module.exports = class extends SkyElement {
}.register();
</script>
</sky-element>
<import src="../../framework/sky-element/sky-element.sky" as="SkyElement" />
<sky-element name="app-search-input">
<template>
<style>
:host {
background-color: #F6F6F6;
padding: 4px;
display: flex;
align-items: center;
font-size: 1.2em;
}
.input-text {
margin-left: 8px;
}
</style>
<span class="input-icon">
<img src="/sky/examples/flights/images/magnifying-glass.png" style="width: 16px">
</span>
<span class="input-text"><t>flights today to dc by price</t></span>
</template>
<script>
module.exports = class extends SkyElement {
attached() {
var sel = window.getSelection();
var input = this.shadowRoot.querySelector('t');
sel.selectAllChildren(input);
}
}.register();
</script>
</sky-element>
<import src="../../framework/sky-element/sky-element.sky" as="SkyElement" />
<sky-element name="app-title">
<template>
<style>
:host {
flex: 1;
display: flex;
align-items: center;
font-size: 22px;
margin-left: 8px;
}
</style>
<content></content>
</template>
<script>
module.exports = class extends SkyElement {
}.register();
</script>
</sky-element>
<import src="../../framework/sky-element/sky-element.sky" as="SkyElement" />
<sky-element name="app-toast">
<template>
<style>
:host {
position: absolute;
bottom: 32px;
right: 32px;
max-width: 55%;
box-shadow: 0px 0px 12px 2px rgba(22, 22, 22, 0.4);
background-image: linear-gradient(#E5D658, #DFCF43);
border: 1px solid #AEA477;
padding: 6px;
border-radius: 2px;
display: flex;
align-items: center;
font-size: 0.8em;
overflow: hidden;
}
.header {
flex-shrink: 0;
margin-right: 6px;
}
.close-box {
position: absolute;
top: 3px;
right: 3px;
width: 1em;
overflow: hidden;
text-align: center;
outline: 1px solid #AEA477;
}
</style>
<div class="header">
<content select=".toast-icon" />
</div>
<div class="content">
<content select=".toast-content" />
</div>
<div class="close-box">
X
</div>
</template>
<script>
module.exports = class extends SkyElement {
}.register();
</script>
</sky-element>
<import src="../../framework/sky-element/sky-element.sky" as="SkyElement" />
<sky-element name="app-toolbar">
<template>
<style>
:host {
background-color: #F6F6F6;
padding: 0 8px;
}
</style>
<content></content>
</template>
<script>
module.exports = class extends SkyElement {
}.register();
</script>
</sky-element>
<import src="../../framework/sky-element/sky-element.sky" as="SkyElement" />
<import src="app-header.sky" />
<import src="app-title.sky" />
<import src="app-menu-button.sky" />
<import src="app-panel.sky" />
<import src="app-panel-header.sky" />
<import src="app-panel-content.sky" />
<import src="app-toolbar.sky" />
<import src="app-search-input.sky" />
<import src="app-scrollable.sky" />
<import src="app-toast.sky" />
<sky-element name="flights-app">
<template>
<style>
* { box-sizing: border-box; }
t, span {
display: inline;
}
:host {
display: flex;
flex-direction: column;
height: 100%;
background-color: #494949;
font-family: "Helvetica", sans-serif;
font-size: 16px;
color: #2B2B2B;
}
.toast-icon {
font-weight: bold;
font-size: 24px;
border-radius: 16px;
width: 32px;
height: 32px;
border: 2px solid black;
line-height: 30px;
text-align: center;
}
.tip-price {
font-weight: bold;
}
.route-result {
margin: 16px 16px 0 16px;
outline: 1px solid black;
}
.route-title {
flex: 1;
}
.airline {
border-radius: 2px;
align-items: center;
padding: 8px;
}
.airline-name {
flex: 1;
}
.airline-logo-image {
width: 50px;
flex-shrink: 0;
}
.airline-best-price {
color: #ABB4B6;
}
.airline-name,
.airline-best-price {
padding: 8px;
}
a {
text-decoration: none;
color: #1155CC;
}
</style>
<app-header>
<app-title>
<t>Search</t>
</app-title>
<app-menu-button>
<img src="/sky/examples/flights/images/menu-white.png" style="width: 18px" />
</app-menu-button>
</app-header>
<app-toolbar>
<app-search-input />
</app-toolbar>
<app-scrollable>
<app-panel class="route-result">
<app-panel-header>
<div class="route-title">
<t>SFO to DCA</t>
</div>
<app-menu-button>
<img src="/sky/examples/flights/images/menu-enabled.png" style="width: 14px" />
</app-menu-button>
</app-panel-header>
<app-panel-content class="airline">
<div class="airline-logo">
<img class="airline-logo-image" src="/sky/examples/flights/images/virgin-america.jpg" />
</div>
<div class="airline-name">
<t>Virgin America</t>
</div>
<div class="airline-best-price">
<t><a>$800</a></t>
</div>
</app-panel-content>
<app-panel-content class="airline">
<div class="airline-logo">
<img class="airline-logo-image" src="/sky/examples/flights/images/ba.jpg" />
</div>
<div class="airline-name">
<t>British Airways</t>
</div>
<div class="airline-best-price">
<t><a>$700</a></t>
</div>
</app-panel-content>
<app-panel-content class="airline">
<div class="airline-logo">
<img class="airline-logo-image" src="/sky/examples/flights/images/united.jpg" />
</div>
<div class="airline-name">
<t>United</t>
</div>
<div class="airline-best-price">
<t><a>$667</a></t>
</div>
</app-panel-content>
<app-panel-content class="airline">
<div class="airline-logo">
<img class="airline-logo-image" src="/sky/examples/flights/images/delta.jpg" />
</div>
<div class="airline-name">
<t>Delta</t>
</div>
<div class="airline-best-price">
<t><a>$450</a></t>
</div>
</app-panel-content>
</app-panel>
<app-panel class="route-result">
<app-panel-header>
<div class="route-title">
<t>SJC to DCA</t>
</div>
<app-menu-button>
<img src="/sky/examples/flights/images/menu-enabled.png" style="width: 14px" />
</app-menu-button>
</app-panel-header>
<app-panel-content class="airline">
<div class="airline-logo">
<img class="airline-logo-image" src="/sky/examples/flights/images/virgin-america.jpg" />
</div>
<div class="airline-name">
<t>Virgin America</t>
</div>
<div class="airline-best-price">
<t><a>$1500</a></t>
</div>
</app-panel-content>
<app-panel-content class="airline">
<div class="airline-logo">
<img class="airline-logo-image" src="/sky/examples/flights/images/jetblue.jpg" />
</div>
<div class="airline-name">
<t>jetBlue</t>
</div>
<div class="airline-best-price">
<t><a>$650</a></t>
</div>
</app-panel-content>
<app-panel-content class="airline">
<div class="airline-logo">
<img class="airline-logo-image" src="/sky/examples/flights/images/united.jpg" />
</div>
<div class="airline-name">
<t>United</t>
</div>
<div class="airline-best-price">
<t><a>$367</a></t>
</div>
</app-panel-content>
</app-panel>
</app-scrollable>
<app-toast>
<div class="toast-icon">
<t>?</t>
</div>
<div class="toast-content">
<t>Flights are <span class="tip-price">$200</span> cheaper tomorrow.</t>
</div>
</app-toast>
</template>
<script>
module.exports = class extends SkyElement {
}.register();
</script>
</sky-element>
#!mojo mojo:sky_viewer
<sky>
<import src="flights-app.sky" as="FlightsAppElement"/>
<style>
sky {
height: 100%;
}
</style>
<flights-app />
</sky>
#!mojo mojo:sky_viewer
<!--
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-->
<import src="/sky/framework/sky-element/sky-element.sky" as="SkyElement" />
<sky-element name="fps-counter" attributes="showHistory:boolean">
<template>
<template if="{{ showHistory }}">
<template repeat="{{ deltas }}">
<div>{{ roundedValue }} ms</div>
</template>
<div>max = {{ max }} ms</div>
</template>
<div>fps = {{ frameRate }} Hz</div>
</template>
<script>
const kMaxDeltaLength = 10;
class Delta {
constructor(value) {
this.value = value;
this.roundedValue = value.toFixed(2);
Object.preventExtensions(this);
}
}
module.exports = class extends SkyElement {
created() {
this.frameRate = "...";
this.max = 0;
this.sum = 0;
this.lastTimeStamp = 0;
this.rafId = 0;
this.deltas = [];
for (var i = 0; i < kMaxDeltaLength; ++i)
this.deltas[i] = new Delta(0);
}
attached() {
this.scheduleTick();
}
detached() {
cancelAnimationFrame(this.rafId);
this.rafId = 0;
}
scheduleTick() {
this.rafId = requestAnimationFrame(this.tick.bind(this));
}
tick(timeStamp) {
this.scheduleTick();
var lastTimeStamp = this.lastTimeStamp;
this.lastTimeStamp = timeStamp;
if (!lastTimeStamp)
return;
var delta = new Delta(timeStamp - lastTimeStamp);
var removedDelta = this.deltas.shift();
this.deltas.push(delta);
this.sum -= removedDelta.value;
this.sum += delta.value;
var avg = this.sum / this.deltas.length;
this.frameRate = (1000 / avg).toFixed(2);
this.max = Math.max(delta.value, this.max).toFixed(2);
}
}.register();
</script>
</sky-element>
SKY MODULE - defines an <element> element
<import src="sky:core" as="sky"/>
<!-- usage:
<element name="tagname">
<style> style here (optional) </style>
<template> shadow tree here (optional) </template>
<script> // optional
module.currentScript.parentNode.setPrototype({
init: function () {
// constructor here
},
// rest of api here
});
</script>
</element>
-->
<script>
module.exports.Element = sky.registerElement(
class extends Element {
static get tagName() { return 'element'; }
constructor (hostModule) {
super(hostModule);
this.state = 'loading';
this.module = hostModule;
this.definedPrototype = sky.Element;
}
setPrototype(prototype) {
this.definedPrototype = prototype;
}
endTagParsedCallback() {
let style = null;
let template = null;
let child = this.firstChild;
while (child && !(style && template)) {
if ((!style) && (child instanceof sky.StyleElement))
style = child;
if ((!template) && (template instanceof sky.TemplateElement))
template = child;
child = child.nextSibling;
}
let tagName = this.getAttribute('name');
let constructorName = tagName.charAt(0).toUpperCase() + tagName.slice(1) + 'Element';
let constructor = function (hostModule) {
super(hostModule);
if (this.init)
this.init();
if (style)
this.shadowRoot.append(style.cloneNode(true));
if (template)
this.shadowRoot.append(template.cloneNode(true));
}
};
if (this.definedPrototype)
constructor.prototype = this.definedPrototype;
else
constructor.prototype = sky.Element;
constructor.tagName = this.getAttribute('name');
constructor.shadow = style || template;
this.module.exports[constructorName] = this.registerElement(constructor);
delete this.definedPrototype;
delete this.module;
this.state = 'loaded';
}
}
);
</script>
SKY MODULE - exports some basic HTML-like elements
<import src="element.sky"/>
<!-- note: accessibility handling is not implemented yet, because the
mojo ax service isn't yet ready -->
<import src="scrollable-block.sky">
<element name=html>
<style>
:host { display: scrollable-block; }
</style>
</element>
<element name=head />
<element name=body />
<element name=p />
<element name=h1>
<style>
:host { margin-top: 0.67em; margin-bottom: 0.67em; font-size: 2.00em; font-weight: bold; }
</style>
</element>
<element name=h2>
<style>
:host { margin-top: 0.83em; margin-bottom: 0.83em; font-size: 1.50em; font-weight: bold; }
</style>
</element>
<element name=h3>
<style>
:host { margin-top: 1.00em; margin-bottom: 1.00em; font-size: 1.17em; font-weight: bold; }
</style>
</element>
<element name=h4>
<style>
:host { margin-top: 1.33em; margin-bottom: 1.33em; font-size: 1.00em; font-weight: bold; }
</style>
</element>
<element name=h5>
<style>
:host { margin-top: 1.67em; margin-bottom: 1.67em; font-size: 0.83em; font-weight: bold; }
</style>
</element>
<element name=h6>
<style>
:host { margin-top: 2.33em; margin-bottom: 2.33em; font-size: 0.67em; font-weight: bold; }
</style>
</element>
<element name=b>
<style>
:host { font-weight: bold; }
</style>
</element>
<element name=data>
<script>
module.currentScript.parentNode.setPrototype({
get value () {
return this.getAttribute('value');
},
set value (newValue) {
this.setAttribute('value', newValue);
},
});
</script>
</element>
<element name=progress>
<template>
<div> ... </div>
</template>
<script>
module.currentScript.parentNode.setPrototype({
...
});
</script>
</element>
<element name=details>
<style>
:host { display: block; }
.outer { border: solid; }
.header { display: inline; }
.summary { display: inline; }
</style>
<template>
<div class="outer">
<div class="header">
<div class="button">OPEN</div>
<div class="summary"><content select="summary"/></div>
</div>
<div class="contents">
<content/>
</div>
</div>
</template>
<script>
module.currentScript.parentNode.setPrototype({
init: function () {
},
open() {
}
});
</script>
</element>
#!mojo mojo:sky
<import src="../framework/htmlish.sky"/>
<html>
<head>
<title>Welcome to Sky</title>
</head>
<body>
<h1>Introduction</h1>
<p>Hello world.</p>
</body>
</html>
......@@ -5,6 +5,6 @@
// found in the LICENSE file.
-->
<sky>
<import src="example-scrollable.sky" />
<example-scrollable />
<import src="scrolling.sky" />
<scrolling />
</sky>
......@@ -7,7 +7,7 @@
<import src="/sky/framework/sky-scrollable.sky" />
<import src="../data/cities.sky" as="cities" />
<sky-element name="example-scrollable">
<sky-element>
<template>
<style>
sky-scrollable {
......@@ -21,8 +21,8 @@
<script>
import "dart:sky";
@Tagname('example-scrollable')
class ExampleScrollable extends SkyElement {
@Tagname('scrolling')
class Scrolling extends SkyElement {
void shadowRootReady() {
Element parent = shadowRoot.querySelector('sky-scrollable');
for (Map city in cities.kData.getRange(0, 300)) {
......@@ -33,6 +33,6 @@ class ExampleScrollable extends SkyElement {
}
}
_init(script) => register(script, ExampleScrollable);
_init(script) => register(script, Scrolling);
</script>
</sky-element>
<script>
import '/sky/framework/shell.dart' as shell;
import 'dart:sky';
import 'package:sky/services/sensors/sensors.mojom.dart';
class MyListener extends SensorListener {
int count = 0;
void onAccuracyChanged(int accuracy) {
print("onAccuracyChanged $accuracy");
}
void onSensorChanged(SensorData data) {
double value = data.values[0] + data.values[1] + data.values[2];
if (value > 40.0) {
document.querySelector('div').textContent =
"Shake count " + (count++).toString();
}
}
MyListener.unbound() {
stub = new SensorListenerStub.unbound()
..delegate = this;
}
SensorListenerStub stub;
}
void main() {
var sensorService = new SensorServiceProxy.unbound();
shell.requestService(sensorService);
var listener = new MyListener.unbound();
sensorService.ptr.addListener(SensorType_ACCELEROMETER, listener.stub);
listener.stub.listen();
}
</script>
<div>Shake me.</div>
SKY MODULE
<import src="sky:core" as="sky"/>
<!--
! this module provides trivial vertical block layout
! no margins, padding, borders, etc
!-->
<script>
module.exports.BlockLayoutManager = class BlockLayoutManager extends sky.LayoutManager {
function layout(width, height) {
if (width == null)
width = this.getIntrinsicWidth().value;
let autoHeight = false;
if (height == null) {
height = 0;
autoHeight = true;
}
this.assumeDimensions(width, height);
let children = this.walkChildren();
let loop = children.next();
let y = 0;
while (!loop.done) {
let child = loop.value;
if (child.needsLayout || child.descendantNeedsLayout) {
let dims = child.layoutManager.layout(width, null);
this.setChildSize(child, dims.width, dims.height);
}
this.setChildPosition(child, 0, y);
y += child.height;
loop = children.next();
}
if (autoHeight)
height = y;
this.markAsLaidOut();
return {
width: width,
height: height,
}
}
function layoutDescendants() {
this.layout(node.width, node.height);
}
function getIntrinsicWidth() {
let width = this.node.getProperty('width');
if (typeof width != 'number') {
// e.g. width: auto
width = 0;
let children = this.walkChildren();
let loop = children.next();
while (!loop.done) {
let child = loop.value;
let childWidth = child.layoutManager.getIntrinsicWidth();
if (width < childWidth.value)
width = childWidth.value;
loop = children.next();
}
}
return super(width); // applies and provides our own min-width/max-width rules
}
function getIntrinsicHeight() {
let height = this.node.getProperty('height');
if (typeof height != 'number') {
// e.g. height: auto
height = 0;
let children = this.walkChildren();
let loop = children.next();
while (!loop.done) {
let child = loop.value;
let childHeight = child.layoutManager.getIntrinsicHeight();
if (height < childHeight.value)
height = childHeight.value;
loop = children.next();
}
}
return super(height); // applies and provides our own min-height/max-height rules
}
}
sky.registerLayoutManager('block', module.exports.BlockLayoutManager);
</script>
#!mojo mojo:sky
<import src="sky:core" as="sky"/>
<script>
class BeehiveLayoutManager extends sky.LayoutManager {
function layout(width, height) {
if (width == null)
width = this.getIntrinsicWidth().value;
let autoHeight = false;
if (height == null) {
height = 0;
autoHeight = true;
}
this.assumeDimensions(width, height);
let cellCount = this.node.getProperty('beehive-count');
let cellDim = width / cellCount;
let children = this.walkChildren();
let loop = children.next();
let x = 0;
let y = 0;
while (!loop.done) {
let child = loop.value;
if (child.needsLayout) {
child.layoutManager.layout(cellDim, cellDim);
// we ignore the size the child reported from layout(), and force it to the cell dimensions
this.setChildSize(child, cellDim, cellDim);
} else if (child.descendantNeedsLayout) {
child.layoutManager.layoutDescendants();
this.setChildSize(child, cellDim, cellDim);
}
this.setChildPosition(child, x * cellDim + (y % 2) * cellDim/2, y * 3/4 * cellDim);
x += 1;
if (x > cellCount) {
y += 1;
x = 0;
}
loop = children.next();
}
if (height == 0)
height = (1 + y * 3/4) * cellDim;
this.markAsLaidOut();
return {
width: width,
height: height,
}
}
function getIntrinsicHeight() {
let height = this.node.getProperty('height');
if (typeof height != 'number') {
// e.g. height: auto
width = this.getIntrinsicWidth().value;
let cellCount = this.node.getProperty('beehive-count');
let cellDim = width / cellCount;
let children = this.walkChildren();
let loop = children.next();
let childCount = 0;
while (!loop.done) {
childCount += 1;
loop.next();
}
if (childCount > 0)
height = cellDim * (1/4 + Math.ceil(childCount / cellCount) * 3/4);
else
height = 0;
}
return super(height); // does the equivalent of getIntrinsicWidth() above, applying min-height etc
}
function paintChildren(canvas) {
let width = this.node.width;
let cellCount = this.node.getProperty('beehive-count');
let cellDim = width / cellCount;
let children = this.walkChildren();
let loop = children.next();
while (!loop.done) {
let child = loop.value;
canvas.save();
try {
canvas.beginPath();
canvas.moveTo(child.x, child.y + cellDim/4);
canvas.lineTo(child.x + cellDim/2, child.y);
canvas.lineTo(child.x + cellDim, child.y + cellDim/4);
canvas.lineTo(child.x + cellDim, child.y + 3*cellDim/4);
canvas.lineTo(child.x + cellDim/2, child.y + cellDim);
canvas.moveTo(child.x, child.y + 3*cellDim/4);
canvas.closePath();
canvas.clip();
canvas.paintChild(child);
} finally {
canvas.restore();
}
loop = children.next();
}
}
function inHex(topLeftX, topLeftY, width, height, hitX, hitY) {
let centerX = topLeftX - width/2;
let absCenteredHitX = Math.abs(hitX - centerX);
if (absCenteredHitX > width/2)
return false;
let centerY = topLeftY - height/2;
let absCenteredHitY = Math.abs(hitY - centerY);
if (absCenteredHitY > height/2)
return false;
if (absCenteredHitY < height * absCenteredHitX / (2 * width) + height / 2)
return true;
return false;
}
function hitTest(x, y) {
let cellCount = this.node.getProperty('beehive-count');
let cellDim = width / cellCount;
let children = this.walkChildren();
let loop = children.next();
while (!loop.done) {
let child = loop.value;
if (this.inHex(child.x, child.y, child.width, child.height, x, y))
return child.layoutManager.hitTest(x-child.x, y-child.y);
loop = children.next();
}
return this.node;
}
}
sky.registerLayoutManager('beehive', BeehiveLayoutManager);
let BeehiveCountStyleGrammar = new StyleGrammar();
BeehiveCountStyleGrammar.addParser((tokens) => {
let token = tokens.next();
if (token.done)
throw new Error();
if (token.value.kind != 'number')
throw new Error();
if (token.value.value <= 0)
throw new Error();
if (Math.trunc(token.value.value) != token.value.value) // is integer
throw new Error();
return new NumericStyleValue(token.value.value);
});
sky.registerProperty({
name: 'beehive-count',
type: BeehiveCountStyleGrammar,
inherits: true,
initialValue: 5,
needsLayout: true,
});
</script>
<style>
div { display: beehive; beehive-count: 3; }
</style>
<div>
<t>Hello</t>
<t>World</t>
<t>How</t>
<t>Are</t>
<t>You</t>
<t>Today?</t>
</div>
SKY MODULE
<!-- this is part of sky:core -->
<script>
// "internals" is an object only made visible to this module that exports stuff implemented in C++
module.exports.registerProperty = internals.registerProperty;
internals.registerLayoutManager('none', null);
module.exports.LayoutManager = internals.LayoutManager;
module.exports.InlineLayoutManager = internals.InlineLayoutManager;
internals.registerLayoutManager('inline', internals.InlineLayoutManager);
module.exports.ParagraphLayoutManager = internals.ParagraphLayoutManager;
internals.registerLayoutManager('paragraph', internals.ParagraphLayoutManager);
module.exports.BlockLayoutManager = internals.BlockLayoutManager;
internals.registerLayoutManager('block', internals.BlockLayoutManager);
let displayTypes = new Map();
module.exports.registerLayoutManager = function registerLayoutManager(displayValue, layoutManagerConstructor) {
// TODO(ianh): apply rules for type-checking displayValue is a String
// TODO(ianh): apply rules for type-checking layoutManagerConstructor implements the LayoutManagerConstructor interface (or is null)
if (displayTypes.has(displayValue))
throw new Error();
displayTypes.set(displayValue, layoutManagerConstructor);
};
module.exports.DisplayStyleGrammar = new StyleGrammar(); // value is null or a LayoutManagerConstructor
module.exports.DisplayStyleGrammar.addParser((tokens) => {
let token = tokens.next();
if (token.done)
throw new Error();
if (token.value.kind != 'identifier')
throw new Error();
if (!displayTypes.has(token.value.value))
throw new Error();
return {
value: displayTypes.get(token.value.value),
}
});
internals.registerProperty({
name: 'display',
type: module.exports.DisplayStyleGrammar,
inherits: false,
initialValue: internals.BlockLayoutManager,
needsLayout: true,
});
module.exports.PositiveLengthStyleGrammar = new StyleGrammar(); // value is a ParsedValue whose value (once resolved) is a number in 96dpi pixels, >=0
module.exports.PositiveLengthStyleGrammar.addParser((tokens) => {
// just handle "<number>px"
let token = tokens.next();
if (token.done)
throw new Error();
if (token.value.kind != 'dimension')
throw new Error();
if (token.value.unit != 'px')
throw new Error();
if (token.value.value < 0)
throw new Error();
return {
value: token.value.value;
};
});
internals.registerProperty({
name: 'min-width',
type: module.exports.PositiveLengthStyleGrammar,
inherits: false,
initialValue: 0,
needsLayout: true,
});
internals.registerProperty({
name: 'min-height',
type: module.exports.PositiveLengthStyleGrammar,
inherits: false,
initialValue: 0,
needsLayout: true,
});
module.exports.PositiveLengthOrAutoStyleGrammar = new StyleGrammar(); // value is a ParsedValue whose value (once resolved) is either a number in 96dpi pixels (>=0) or null (meaning 'auto')
module.exports.PositiveLengthOrAutoStyleGrammar.addParser((tokens) => {
// handle 'auto'
let token = tokens.next();
if (token.done)
throw new Error();
if (token.value.kind != 'identifier')
throw new Error();
if (token.value.value != 'auto')
throw new Error();
return {
value: null,
};
});
module.exports.PositiveLengthOrAutoStyleGrammar.addParser((tokens) => {
return module.exports.PositiveLengthStyleGrammar.parse(tokens);
});
internals.registerProperty({
name: 'width',
type: module.exports.PositiveLengthOrAutoStyleGrammar,
inherits: false,
initialValue: null,
needsLayout: true,
});
internals.registerProperty({
name: 'height',
type: module.exporets.PositiveLengthOrAutoStyleGrammar,
inherits: false,
initialValue: null,
needsLayout: true,
});
module.exports.PositiveLengthOrInfinityStyleGrammar = new StyleGrammar(); // value is a ParsedValue whose value (once resolved) is either a number in 96dpi pixels (>=0) or Infinity
module.exports.PositiveLengthOrInfinityStyleGrammar.addParser((tokens) => {
// handle 'infinity'
let token = tokens.next();
if (token.done)
throw new Error();
if (token.value.kind != 'identifier')
throw new Error();
if (token.value.value != 'infinity')
throw new Error();
return {
value: Infinity,
};
});
module.exports.PositiveLengthOrInfinityStyleGrammar.addParser((tokens) => {
return module.exports.PositiveLengthStyleGrammar.parse(tokens);
});
internals.registerProperty({
name: 'width',
type: module.exports.PositiveLengthOrInfinityStyleGrammar,
inherits: false,
initialValue: Infinity,
needsLayout: true,
});
internals.registerProperty({
name: 'height',
type: module.exporets.PositiveLengthOrInfinityStyleGrammar,
inherits: false,
initialValue: Infinity,
needsLayout: true,
});
</script>
SKY MODULE
<import src="sky:core" as="sky"/>
<script>
// display: toolbar;
// toolbar-spacing: <length>
// display: spring; // remaining space is split equally amongst the springs
// children are vertically centered, layout out left-to-right with toolbar-spacing space between them
// last child is hidden by default unless there's not enough room for the others, then it's shown last, right-aligned
module.exports.SpringLayoutManager = class SpringLayoutManager extends sky.LayoutManager { }
sky.registerLayoutManager('spring', module.exports.SpringLayoutManager);
sky.registerProperty({
name: 'toolbar-spacing',
type: sky.PositiveLengthStyleGrammar,
inherits: true,
initialValue: 8,
needsLayout: true,
});
module.exports.ToolbarLayoutManager = class ToolbarLayoutManager extends sky.LayoutManager {
constructor (styleNode) {
super(styleNode);
this.showingOverflow = false;
this.firstSkippedChild = null;
this.overflowChild = null;
}
function layout(width, height) {
let children = null;
let loop = null;
if (height == null)
height = this.getIntrinsicHeight().value;
if (width == null)
this.assumeDimensions(0, height);
else
this.assumeDimensions(width, height);
let spacing = this.node.getProperty('toolbar-spacing');
if (typeof spacing != 'number')
spacing = 0;
this.overflowChild = null;
this.firstSkippedChild = null;
// layout children and figure out whether we need to truncate the child list and show the overflow child
let springCount = 0;
let minX = 0;
let overflowChildWidth = 0;
let pendingSpacing = 0;
children = this.walkChildren();
loop = children.next();
while (!loop.done) {
let child = loop.value;
let dims = null;
if (child.layoutManager instanceof module.exports.SpringLayoutManager) {
springCount += 1;
pendingSpacing = spacing; // not +=, because we only have one extra spacing per batch of springs
} else {
if (child.needsLayout || child.descendantNeedsLayout) {
childHeight = child.layoutManager.getIntrinsicHeight();
if (childHeight.value < height)
childHeight = childHeight.value;
else
childHeight = height;
dims = child.layoutManager.layout(null, height);
this.setChildSize(child, dims.width, dims.height);
} else {
dims = {
width: child.width,
height: child.height,
};
}
loop = children.next();
if (!loop.done) {
if (minX > 0)
minX += spacing + pendingSpacing;
minX += dims.width;
pendingSpacing = 0;
} else {
overflowChildWidth = spacing + dims.width;
this.overflowChild = child;
}
}
}
// figure out the spacing
this.showingOverflow = false;
let springSize = 0;
if (width != null) {
if (minX <= width) {
if (springCount > 0)
springSize = (width - minX) / sprintCount;
} else {
this.showingOverflow = true;
}
} else {
width = minX;
}
// position the children
// TODO(ianh): support rtl toolbars
let x = 0;
let lastWasNonSpring = false;
children = this.walkChildren();
loop = children.next();
while (!loop.done) {
let child = loop.value;
if (child.layoutManager instanceof module.exports.SpringLayoutManager) {
x += springSize;
if (lastWasNonSpring)
x += spacing;
lastWasNonSpring = false;
} else {
if (!loop.done) {
if (x + child.width + overflowChildWidth > width) {
this.firstSkippedChild = child;
break; // don't display any more children
}
this.setChildPosition(child, x, (height - child.height)/2);
x += child.width + spacing;
lastWasNonSpring = true;
} else {
// assert: this.showingOverflow == false
}
}
}
if (this.showingOverflow)
this.setChildPosition(this.overflowChild, width-this.overflowChild.width, (height - this.overflowChild.height)/2);
else
this.firstSkippedChild = this.overflowChild;
this.markAsLaidOut();
return {
width: width,
height: height,
}
}
function layoutDescendants() {
this.layout(node.width, node.height);
}
function getIntrinsicWidth() {
let width = this.node.getProperty('width');
if (typeof width != 'number') {
let spacing = this.node.getProperty('toolbar-spacing');
if (typeof spacing != 'number')
spacing = 0;
width = 0;
let children = this.walkChildren();
let loop = children.next();
// we exclude the last child because at our ideal width we wouldn't need it
let last1 = null; // last one
let last2 = null; // one before the last one
while (!loop.done) {
if (last1)
width += last1.layoutManager.getIntrinsicWidth().value;
if (last2)
width += spacing;
last2 = last1;
last1 = loop.value;
loop = children.next();
}
}
return super(width); // applies and provides our own min-width/max-width rules
}
function getIntrinsicHeight() {
// we grow our minimum height to be no smaller than the children's
let result = super();
let determineHeight = false;
let heightProperty = this.node.getProperty('height');
if (typeof heightProperty != 'number')
determineHeight = true;
let children = this.walkChildren();
let loop = children.next();
// here we include the last child so that if it pops in our height doesn't change
while (!loop.done) {
let child = loop.value;
let childHeight = child.layoutManager.getIntrinsicHeight();
if (determineHeight) {
if (result.value < childHeight.value)
result.value = childHeight.value;
}
if (result.minimum < childHeight.minimum)
result.minimum = childHeight.minimum;
loop = children.next();
}
if (result.minimum > result.maximum)
result.maximum = result.minimum;
if (result.value > result.maximum)
result.value = result.maximum;
if (result.value < result.minimum)
result.value = result.minimum;
return result;
}
function paintChildren(canvas) {
let width = this.node.width;
let children = this.walkChildren();
let loop = children.next();
while ((!loop.done) && (loop.value != this.firstSkippedChild))
canvas.paintChild(loop.value);
if (this.showingOverflow)
canvas.paintChild(this.overflowChild);
}
function inChild(child, x, y) {
return (x >= child.x) && (y >= child.y) && (x < child.x+child.width) && (y < child.y+child.height);
}
function hitTest(x, y) {
let children = this.walkChildrenBackwards();
let loop = children.next();
while ((!loop.done) && (loop.value != this.firstSkippedChild))
if (this.inChild(loop.value, x, y))
return loop.value;
if (this.showingOverflow)
if (this.inChild(this.overflowChild, x, y))
return this.overflowChild;
return this.node;
}
}
sky.registerLayoutManager('toolbar', module.exports.ToolbarLayoutManager);
</script>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册