blob: 0582ef32081ae89170f7de3d23a07cffc9a62148 [file] [log] [blame]
// Copyright (C) 2015 The Android Open Source Project
//
// 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.
package client
import (
"fmt"
"reflect"
"sort"
"android.googlesource.com/platform/tools/gpu/binary/schema"
"android.googlesource.com/platform/tools/gpu/service/path"
"github.com/google/gxui"
"github.com/google/gxui/math"
)
const kStateAdapterNodeHeight = 18
func createControls(appCtx *ApplicationContext, name string, p path.Path, v interface{}) gxui.Control {
layout := appCtx.theme.CreateLinearLayout()
layout.SetDirection(gxui.LeftToRight)
addLabel := func(format string, args ...interface{}) gxui.Label {
label := appCtx.theme.CreateLabel()
label.SetText(fmt.Sprintf(format, args...))
label.SetMargin(math.ZeroSpacing)
label.SetMultiline(false)
layout.AddChild(label)
return label
}
label := addLabel("%s: ", name)
appCtx.toolTipController.AddToolTip(label, 0.7, func(math.Point) gxui.Control {
return createLabel(appCtx, p.Path(), gxui.White)
})
if v != nil {
c := createField(appCtx, p, nil, v)
if c != nil {
layout.AddChild(c)
}
}
return layout
}
type StateAdapterNode struct {
appCtx *ApplicationContext
name string
value interface{}
path path.Value
item string
children StateAdapterNodeList
parent *StateAdapterNode
}
type StateAdapterNodeList []*StateAdapterNode
func (l StateAdapterNodeList) Len() int { return len(l) }
func (l StateAdapterNodeList) Less(a, b int) bool { return l[a].name < l[b].name }
func (l StateAdapterNodeList) Swap(a, b int) { l[a], l[b] = l[b], l[a] }
func (n *StateAdapterNode) add(name string, value interface{}, path path.Value) {
n.children = append(n.children, &StateAdapterNode{
appCtx: n.appCtx,
name: name,
value: value,
path: path,
item: path.Path(),
parent: n,
})
}
func (n *StateAdapterNode) init() {
n.children = nil
if v, ok := n.value.(*schema.Object); ok {
for i := range v.Fields {
name := v.Type.Fields[i].Name()
n.add(name, v.Fields[i], n.path.Field(name))
}
} else {
v := reflect.ValueOf(n.value)
switch v.Kind() {
case reflect.Array, reflect.Slice:
for i, c := 0, v.Len(); i < c; i++ {
name := fmt.Sprintf("%d", i)
v := v.Index(i)
n.add(name, v.Interface(), n.path.ArrayIndex(uint64(i)))
}
case reflect.Map:
for _, k := range v.MapKeys() {
v := v.MapIndex(k)
k := k.Interface()
name := fmt.Sprintf("%v", k)
n.add(name, v.Interface(), n.path.MapIndex(k))
}
sort.Sort(n.children)
}
}
for _, c := range n.children {
c.init()
}
}
func (n *StateAdapterNode) Count() int {
return len(n.children)
}
func (n *StateAdapterNode) NodeAt(index int) gxui.TreeNode {
return n.children[index]
}
func (n *StateAdapterNode) ItemIndex(item gxui.AdapterItem) int {
// Brute-force search
for i, c := range n.children {
if c.item == item {
return i
}
if c.ItemIndex(item) >= 0 {
return i
}
}
return -1
}
func (n *StateAdapterNode) Item() gxui.AdapterItem {
return n.item
}
func (n *StateAdapterNode) Create(t gxui.Theme) gxui.Control {
if len(n.children) > 0 {
return createControls(n.appCtx, n.name, n.path, nil)
} else {
return createControls(n.appCtx, n.name, n.path, n.value)
}
}
type StateAdapter struct {
StateAdapterNode
gxui.AdapterBase
}
func (r *StateAdapter) Size(theme gxui.Theme) math.Size {
return math.Size{W: math.MaxSize.W, H: kStateAdapterNodeHeight}
}
func NewStateAdapter(appCtx *ApplicationContext) *StateAdapter {
return &StateAdapter{
StateAdapterNode: StateAdapterNode{appCtx: appCtx},
}
}
func (a *StateAdapter) Update(value interface{}, path *path.State) {
a.value = value
a.path = path
a.init()
a.DataReplaced()
}