This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
Doric/doric-js/src/ui/panel.ts

225 lines
6.4 KiB
TypeScript

/*
* Copyright [2019] [Doric.Pub]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { View, Group } from "./view"
import { loge } from '../util/log'
import { Model } from '../util/types'
import { Root } from '../widget/layouts'
import { BridgeContext } from '../runtime/global'
export function NativeCall(target: Panel, propertyKey: string, descriptor: PropertyDescriptor) {
const originVal = descriptor.value
descriptor.value = function () {
const ret = Reflect.apply(originVal, this, arguments)
return ret
}
return descriptor
}
type Frame = { width: number, height: number }
declare function nativeEmpty(): void
export abstract class Panel {
context!: BridgeContext
onCreate() { }
onDestroy() { }
onShow() { }
onHidden() { }
abstract build(rootView: Group): void
private __data__?: object
private __root__ = new Root
private headviews: Map<string, Map<string, View>> = new Map
private onRenderFinishedCallback: Array<() => void> = []
addHeadView(type: string, v: View) {
let map = this.headviews.get(type)
if (map) {
map.set(v.viewId, v)
} else {
map = new Map
map.set(v.viewId, v)
this.headviews.set(type, map)
}
}
allHeadViews() {
return this.headviews.values()
}
removeHeadView(type: string, v: View | string) {
if (this.headviews.has(type)) {
let map = this.headviews.get(type)
if (map) {
if (v instanceof View) {
map.delete(v.viewId)
} else {
map.delete(v)
}
}
}
}
clearHeadViews(type: string) {
if (this.headviews.has(type)) {
this.headviews.delete(type)
}
}
getRootView() {
return this.__root__
}
getInitData() {
return this.__data__
}
@NativeCall
private __init__(frame: Frame, data?: string) {
if (data) {
this.__data__ = JSON.parse(data)
}
this.__root__.width = frame.width
this.__root__.height = frame.height
this.__root__.children.length = 0
this.build(this.__root__)
}
@NativeCall
private __onCreate__() {
this.onCreate()
}
@NativeCall
private __onDestroy__() {
this.onDestroy()
}
@NativeCall
private __onShow__() {
this.onShow()
}
@NativeCall
private __onHidden__(): void {
this.onHidden()
}
@NativeCall
private __build__() {
this.build(this.__root__)
}
@NativeCall
private __response__(viewIds: string[], callbackId: string) {
const v = this.retrospectView(viewIds)
if (v === undefined) {
loge(`Cannot find view for ${viewIds}`)
} else {
const argumentsList: any = [callbackId]
for (let i = 2; i < arguments.length; i++) {
argumentsList.push(arguments[i])
}
return Reflect.apply(v.responseCallback, v, argumentsList)
}
}
private retrospectView(ids: string[]): View | undefined {
return ids.reduce((acc: View | undefined, cur) => {
if (acc === undefined) {
if (cur === this.__root__.viewId) {
return this.__root__
}
for (let map of this.headviews.values()) {
if (map.has(cur)) {
return map.get(cur)
}
}
return undefined
} else {
if (Reflect.has(acc, "subviewById")) {
return Reflect.apply(Reflect.get(acc, "subviewById"), acc, [cur])
}
return acc
}
}, undefined)
}
private nativeRender(model: Model) {
return this.context.callNative("shader", "render", model)
}
private hookBeforeNativeCall() {
if (Environment.platform !== 'web') {
this.__root__.clean()
for (let map of this.headviews.values()) {
for (let v of map.values()) {
v.clean()
}
}
}
}
private hookAfterNativeCall() {
const promises: Promise<any>[] = []
if (Environment.platform !== 'web') {
//Here insert a native call to ensure the promise is resolved done.
nativeEmpty()
if (this.__root__.isDirty()) {
const model = this.__root__.toModel()
promises.push(this.nativeRender(model))
}
for (let map of this.headviews.values()) {
for (let v of map.values()) {
if (v.isDirty()) {
const model = v.toModel()
promises.push(this.nativeRender(model))
}
}
}
} else {
Promise.resolve().then(() => {
if (this.__root__.isDirty()) {
const model = this.__root__.toModel()
promises.push(this.nativeRender(model))
this.__root__.clean()
}
for (let map of this.headviews.values()) {
for (let v of map.values()) {
if (v.isDirty()) {
const model = v.toModel()
promises.push(this.nativeRender(model))
v.clean()
}
}
}
})
}
Promise.all(promises).then(_ => {
this.onRenderFinished()
})
}
onRenderFinished() {
this.onRenderFinishedCallback.forEach(e => {
e()
})
this.onRenderFinishedCallback.length = 0
}
addOnRenderFinishedCallback(cb: () => void) {
this.onRenderFinishedCallback.push(cb)
}
}