From 6416db4dcfc3819d6355c2ce5469f89d4c2c2aec Mon Sep 17 00:00:00 2001 From: Benign X <1341398182@qq.com> Date: Tue, 21 Apr 2026 19:44:32 +0800 Subject: [PATCH] chore(gdb): add widget_specs JSON and spec-driven dashboard rendering - Add widget_specs.json with auto-generated field types and hand-written summary templates, primary fields, and enum name mappings - Generator merges _auto section with hand-written overrides on each run - data_collector outputs widget_specs to dashboard JSON top-level - Update uinspy frontend with spec-driven detail panel and tree summary --- .../lvglgdb/cmds/dashboard/data_collector.py | 55 ++ .../lvglgdb/cmds/dashboard/static/uinspy.html | 103 ++- .../scripts/generators/gen_widget_wrappers.py | 91 ++- .../gdb/scripts/generators/widget_specs.json | 751 ++++++++++++++++++ 4 files changed, 961 insertions(+), 39 deletions(-) create mode 100644 scripts/gdb/scripts/generators/widget_specs.json diff --git a/scripts/gdb/lvglgdb/cmds/dashboard/data_collector.py b/scripts/gdb/lvglgdb/cmds/dashboard/data_collector.py index 3ed25f969f..bc8c648e4b 100644 --- a/scripts/gdb/lvglgdb/cmds/dashboard/data_collector.py +++ b/scripts/gdb/lvglgdb/cmds/dashboard/data_collector.py @@ -58,6 +58,8 @@ def collect_all() -> dict: # Registry-driven simple collectors for dict_key, accessor, label in SIMPLE_REGISTRY: data[dict_key] = _collect_simple(lvgl, dict_key, accessor, label) + # Widget specs (global lookup table for uinspy rendering) + data["widget_specs"] = _collect_widget_specs(data.get("object_trees", [])) return data @@ -220,3 +222,56 @@ def _collect_subjects_from_obj(obj, seen, result): except CorruptedError: # Children pointer unreadable: stop traversal for this subtree pass + + +def _collect_widget_specs(object_trees: list) -> dict: + """Build widget_specs dict for all class_names found in object trees.""" + import json as _json + from pathlib import Path + + specs_path = Path(__file__).parent.parent.parent.parent / "scripts" / "generators" / "widget_specs.json" + if not specs_path.exists(): + return {} + + all_specs = _json.loads(specs_path.read_text()) + + # Collect all class_names from object trees + seen = set() + + def _walk(node): + cn = node.get("class_name", "") + if cn: + seen.add(cn) + for child in node.get("children", []): + _walk(child) + + for tree in object_trees: + for screen in tree.get("screens", []): + _walk(screen) + + # Build output: merge _auto + enums into flat fields spec + result = {} + for cn in seen: + raw = all_specs.get(cn) or all_specs.get("lv_" + cn) + if not raw: + continue + auto_fields = raw.get("_auto", {}).get("fields", {}) + enums = raw.get("enums", {}) + fields = {} + for fname, ftype in auto_fields.items(): + entry = {"type": ftype} + if fname in enums: + entry["type"] = "enum" + entry["names"] = enums[fname] + fields[fname] = entry + + spec = {} + if raw.get("summary_tpl") is not None: + spec["summary_tpl"] = raw["summary_tpl"] + if raw.get("primary"): + spec["primary"] = raw["primary"] + if fields: + spec["fields"] = fields + if spec: + result[cn] = spec + return result diff --git a/scripts/gdb/lvglgdb/cmds/dashboard/static/uinspy.html b/scripts/gdb/lvglgdb/cmds/dashboard/static/uinspy.html index 8066e28135..20ea6b6749 100644 --- a/scripts/gdb/lvglgdb/cmds/dashboard/static/uinspy.html +++ b/scripts/gdb/lvglgdb/cmds/dashboard/static/uinspy.html @@ -1137,6 +1137,10 @@ ui-topbar .topbar-search::placeholder { .obj-node > summary:hover { background-color: var(--color-hover-summary); } +.obj-summary-hint { + margin-left: calc(var(--spacing) * 1); + color: var(--color-overlay0); +} .obj-node.obj-selected > summary { background-color: var(--color-nav-active-bg); outline: 1px solid var(--blue); @@ -1815,7 +1819,27 @@ function selectObj(addr) { document.getElementById("obj-" + prev)?.classList.remove("obj-selected"); selectedAddr.val = addr, document.getElementById("obj-" + addr)?.classList.add("obj-selected"); } -var dashData = signal(null); +var dashData = signal(null), widgetSpecs = {}; +function setWidgetSpecs(specs) { + widgetSpecs = specs; +} +function getWidgetSpec(className) { + return widgetSpecs[className] || widgetSpecs["lv_" + className]; +} +function widgetSummary(className, wd) { + if (!wd) + return ""; + let spec = getWidgetSpec(className); + if (!spec?.summary_tpl) + return ""; + return spec.summary_tpl.replace(/\{(\w+)\}/g, (_, k) => { + let v = wd[k]; + if (v == null) + return ""; + let s = String(v); + return s.length > 24 ? s.slice(0, 24) + "…" : s; + }); +} function countObjects(trees) { let n = 0; function walk(obj) { @@ -2100,9 +2124,17 @@ function buildCard(item, config) { } // src/builders/obj-tree.ui.ts -function formatWdVal(v) { +function formatField(v, spec) { if (v == null) return "-"; + if (spec) { + if (spec.type === "enum" && spec.names && typeof v === "number") + return spec.names[v] ?? String(v); + if (spec.type === "bool") + return v ? "true" : "false"; + if (spec.type === "string" && typeof v === "string") + return v.length > 60 ? '"' + v.slice(0, 60) + '…"' : '"' + v + '"'; + } if (typeof v === "object") return JSON.stringify(v); return String(v); @@ -2112,7 +2144,15 @@ function renderObjTree(obj, depth = 0) { if (det.className = "obj-node", obj.addr) det.id = "obj-" + obj.addr; let sum = document.createElement("summary"); - if (sum.style.setProperty("--depth-color", DEPTH_COLORS[depth % DEPTH_COLORS.length]), sum.textContent = (obj.name ? obj.name + " " : "") + (obj.class_name || "obj"), obj.flags_list?.includes("HIDDEN")) + sum.style.setProperty("--depth-color", DEPTH_COLORS[depth % DEPTH_COLORS.length]); + let nameText = (obj.name ? obj.name + " " : "") + (obj.class_name || "obj"); + sum.textContent = nameText; + let hint = widgetSummary(obj.class_name, obj.widget_data); + if (hint) { + let span = document.createElement("span"); + span.className = "obj-summary-hint", span.textContent = hint, sum.appendChild(span); + } + if (obj.flags_list?.includes("HIDDEN")) sum.textContent += " \uD83D\uDC41‍\uD83D\uDDE8"; if (det.appendChild(sum), obj.addr) objDataMap[obj.addr] = obj, registerHL(obj.addr, det), sum.addEventListener("mouseenter", () => highlightObj(obj.addr)), sum.addEventListener("mouseleave", () => clearHighlight()), sum.addEventListener("click", (e) => { @@ -2187,23 +2227,25 @@ function renderObjDetail(addr, panel) { refSec.appendChild(kvPair("user_data", obj.user_data)); if (obj.name) refSec.appendChild(kvPair("name", obj.name)); - if (refSec.appendChild(kvPair("children", String(obj.child_count || 0))), refSec.appendChild(kvPair("styles", String(obj.style_count || 0))), panel.appendChild(refSec), obj.scroll || obj.ext_click_pad || obj.ext_draw_size || obj.scrollbar_mode || obj.layer_type || obj.w_layout || obj.h_layout) { + refSec.appendChild(kvPair("children", String(obj.child_count || 0))), refSec.appendChild(kvPair("styles", String(obj.style_count || 0))), panel.appendChild(refSec); + let _n = (v) => v != null; + if (obj.scroll || _n(obj.ext_click_pad) || _n(obj.ext_draw_size) || _n(obj.scrollbar_mode) || _n(obj.layer_type) || obj.w_layout || obj.h_layout) { let layoutSec = el("div", "detail-section"); if (layoutSec.appendChild(el("div", "detail-section-title", "Layout & Scroll")), obj.scroll) layoutSec.appendChild(kvPair("scroll", `${obj.scroll.x}, ${obj.scroll.y}`)); - if (obj.ext_click_pad) + if (_n(obj.ext_click_pad)) layoutSec.appendChild(kvPair("ext_click_pad", String(obj.ext_click_pad))); - if (obj.ext_draw_size) + if (_n(obj.ext_draw_size)) layoutSec.appendChild(kvPair("ext_draw_size", String(obj.ext_draw_size))); - if (obj.scrollbar_mode != null) + if (_n(obj.scrollbar_mode)) layoutSec.appendChild(kvPair("scrollbar_mode", String(obj.scrollbar_mode))); - if (obj.scroll_dir != null) + if (_n(obj.scroll_dir)) layoutSec.appendChild(kvPair("scroll_dir", String(obj.scroll_dir))); - if (obj.scroll_snap_x) + if (_n(obj.scroll_snap_x)) layoutSec.appendChild(kvPair("scroll_snap_x", String(obj.scroll_snap_x))); - if (obj.scroll_snap_y) + if (_n(obj.scroll_snap_y)) layoutSec.appendChild(kvPair("scroll_snap_y", String(obj.scroll_snap_y))); - if (obj.layer_type) + if (_n(obj.layer_type)) layoutSec.appendChild(kvPair("layer_type", String(obj.layer_type))); if (obj.w_layout) layoutSec.appendChild(kvPair("w_layout", "true")); @@ -2226,38 +2268,23 @@ function renderObjDetail(addr, panel) { intSec.appendChild(wrap), panel.appendChild(intSec); } if (obj.widget_data && Object.keys(obj.widget_data).length) { - let wd = obj.widget_data, primary = { - lv_label: ["text", "long_mode", "recolor"], - lv_image: ["src", "w", "h", "rotation", "scale_x", "scale_y", "align"], - lv_bar: ["cur_value", "min_value", "max_value", "start_value", "mode"], - lv_slider: ["cur_value", "min_value", "max_value", "start_value", "mode", "dragging"], - lv_arc: ["value", "min_value", "max_value", "rotation", "type"], - lv_switch: ["anim_state", "orientation"], - lv_checkbox: ["txt"], - lv_dropdown: ["options", "option_cnt", "sel_opt_id", "dir"], - lv_textarea: ["placeholder_txt", "max_length", "pwd_show_time"], - lv_tabview: ["tab_cur", "tab_pos", "tab_bar_size"], - lv_roller: ["option_cnt", "sel_opt_id", "mode"], - lv_chart: ["point_cnt", "hdiv_cnt", "vdiv_cnt", "type"], - lv_scale: ["mode", "range_min", "range_max", "total_tick_count", "angle_range", "rotation"], - lv_spinner: ["duration", "angle"], - lv_keyboard: ["mode", "popovers"], - lv_led: ["color", "bright"], - lv_spinbox: ["value", "range_min", "range_max", "step", "digit_count", "dec_point_pos"], - lv_calendar: ["today", "showed_date"], - lv_table: ["col_cnt", "row_cnt"], - lv_buttonmatrix: ["btn_cnt", "row_cnt", "btn_id_sel", "one_check"] - }[obj.class_name] || [], allKeys = Object.keys(wd), priKeys = allKeys.filter((k) => primary.includes(k)), advKeys = allKeys.filter((k) => !primary.includes(k)), wdSec = el("div", "detail-section"); + let wd = obj.widget_data, spec = getWidgetSpec(obj.class_name), fieldSpecs = spec?.fields || {}, primary = spec?.primary || [], allKeys = Object.keys(wd), priKeys = primary.filter((k) => (k in wd)), advKeys = allKeys.filter((k) => !priKeys.includes(k)), wdSec = el("div", "detail-section"); wdSec.appendChild(el("div", "detail-section-title", "Widget · " + obj.class_name)); + let renderField = (k) => { + let fs = fieldSpecs[k], raw = wd[k]; + wdSec.appendChild(kvPair(k, formatField(raw, fs))); + }; for (let k of priKeys.length ? priKeys : allKeys.slice(0, 6)) - wdSec.appendChild(kvPair(k, formatWdVal(wd[k]))); + renderField(k); let rest = priKeys.length ? advKeys : allKeys.slice(6); if (rest.length) { let toggle = el("details", "detail-adv-toggle"); toggle.appendChild(el("summary", "detail-adv-summary", `${rest.length} more fields`)); let inner = el("div", ""); - for (let k of rest) - inner.appendChild(kvPair(k, formatWdVal(wd[k]))); + for (let k of rest) { + let fs = fieldSpecs[k]; + inner.appendChild(kvPair(k, formatField(wd[k], fs))); + } toggle.appendChild(inner), wdSec.appendChild(toggle); } panel.appendChild(wdSec); @@ -3223,7 +3250,7 @@ class UiDashboard extends BaseComponent { let data = dashData.val; if (!data) return; - grid.innerHTML = ""; + setWidgetSpecs(data.widget_specs || {}), grid.innerHTML = ""; let bento = el("div", "bento"), objCount = countObjects(data.object_trees || []); STAT_DEFS.forEach((s) => { let val = s.key === "_objects" ? objCount : data[s.key]?.length || 0; @@ -3235,7 +3262,7 @@ class UiDashboard extends BaseComponent { customElements.define("ui-dashboard", UiDashboard); // src/app.ts -document.getElementById("about").innerHTML = `uinspy v${"0.4.6"}·Built ${"2026-04-02 13:52 GMT+8"}·${"07131c2"}·${"Canvas2D"}·GitHub·LVGL·MIT`; +document.getElementById("about").innerHTML = `uinspy v${"0.4.6"}·Built ${"2026-04-21 19:26 GMT+8"}·${"1b5dc67"}·${"Canvas2D"}·GitHub·LVGL·MIT`; \ No newline at end of file diff --git a/scripts/gdb/scripts/generators/gen_widget_wrappers.py b/scripts/gdb/scripts/generators/gen_widget_wrappers.py index 9db1dddb25..a44f28e6ef 100644 --- a/scripts/gdb/scripts/generators/gen_widget_wrappers.py +++ b/scripts/gdb/scripts/generators/gen_widget_wrappers.py @@ -19,6 +19,7 @@ Usage (from the GDB script root): import re import sys +import json from pathlib import Path from dataclasses import dataclass, field as dc_field @@ -28,6 +29,7 @@ LVGL_SRC = Path(__file__).parent.parent.parent.parent.parent / "src" LVGL_INC = Path(__file__).parent.parent.parent.parent.parent / "include" / "lvgl" WIDGETS_DIR = LVGL_SRC / "widgets" OUTPUT_DIR = Path(__file__).parent.parent.parent / "lvglgdb" / "lvgl" / "widgets" +SPECS_YAML = Path(__file__).parent / "widget_specs.yaml" SIMPLE_INT_TYPES = { "int8_t", "int16_t", "int32_t", "int64_t", @@ -268,6 +270,33 @@ def _field_expr(f: StructField) -> str | None: return None +def _field_type_name(f: StructField) -> str | None: + """Infer a spec type name for a field. Returns None to skip.""" + if f.is_array: + return None + if f.is_obj_pointer: + return "pointer" + if f.is_string: + return "string" + if f.is_pointer: + return "pointer" + if f.c_type == "lv_color_t": + return "color" + if f.c_type == "lv_area_t": + return "area" + if f.c_type == "lv_point_t": + return "point" + if f.is_bitfield: + return "bool" if f.bitfield_width == 1 else f"enum:{f.bitfield_width}" + if f.c_type == "bool": + return "bool" + if f.c_type in SIMPLE_INT_TYPES or f.c_type.startswith(("uint", "int")): + return "int" + if f.c_type.startswith("lv_") and f.c_type.endswith("_t"): + return "int" + return None + + def _topo_sort(widgets: dict[str, WidgetDef]) -> list[WidgetDef]: result, visited = [], set() def visit(w): @@ -496,6 +525,61 @@ def gen_init(ordered: list[WidgetDef]) -> str: return "\n".join(lines) +SPECS_JSON = Path(__file__).parent / "widget_specs.json" + + +def _build_auto_spec(wdef: WidgetDef) -> dict: + """Build the _auto section for a widget from parsed fields.""" + fields = {} + for f in wdef.fields: + t = _field_type_name(f) + if t: + fields[f.name] = t + return {"fields": fields} + + +def update_specs_json(widgets: dict[str, WidgetDef]) -> dict: + """Merge auto-generated field info with hand-written specs. + + - _auto section is always regenerated + - Hand-written keys (summary_tpl, primary, enums) are preserved + - New widgets get an empty template + - Removed widgets get _removed: true + """ + existing = {} + if SPECS_JSON.exists(): + existing = json.loads(SPECS_JSON.read_text()) + + result = {} + # Preserve _comment + if "_comment" in existing: + result["_comment"] = existing["_comment"] + else: + result["_comment"] = "Auto-generated + hand-written widget specs. _auto is regenerated; other keys are preserved." + + seen = set() + for wdef in sorted(widgets.values(), key=lambda w: w.c_class_name): + key = wdef.c_class_name # e.g. "lv_label" + seen.add(key) + old = existing.get(key, {}) + entry = {"_auto": _build_auto_spec(wdef)} + # Preserve hand-written keys + for k in ("summary_tpl", "primary", "enums"): + if k in old: + entry[k] = old[k] + result[key] = entry + + # Mark removed widgets + for key, val in existing.items(): + if key.startswith("_") or key in seen: + continue + val["_removed"] = True + result[key] = val + + SPECS_JSON.write_text(json.dumps(result, indent=2, ensure_ascii=False) + "\n") + return result + + def main(): widgets = parse_widgets() ordered = _topo_sort(widgets) @@ -504,6 +588,11 @@ def main(): for w in ordered: print(f" {w.module_name}.py: {w.class_name}({w.parent_class_name}) — {len(w.fields)} fields") + # Update specs JSON (merge auto + hand-written) + specs = update_specs_json(widgets) + new_count = sum(1 for k, v in specs.items() if not k.startswith("_") and "summary_tpl" not in v) + print(f"\nUpdated {SPECS_JSON.name}: {len(specs) - 1} widgets ({new_count} need manual spec)") + OUTPUT_DIR.mkdir(parents=True, exist_ok=True) # _helpers.py @@ -517,7 +606,7 @@ def main(): # __init__.py (OUTPUT_DIR / "__init__.py").write_text(gen_init(ordered)) - print(f"\nGenerated {len(ordered) + 2} files in {OUTPUT_DIR}/") + print(f"Generated {len(ordered) + 2} files in {OUTPUT_DIR}/") if __name__ == "__main__": diff --git a/scripts/gdb/scripts/generators/widget_specs.json b/scripts/gdb/scripts/generators/widget_specs.json new file mode 100644 index 0000000000..02b1bc4603 --- /dev/null +++ b/scripts/gdb/scripts/generators/widget_specs.json @@ -0,0 +1,751 @@ +{ + "_comment": "Widget spec overrides: summary templates, primary fields, enum names. Generator merges with auto-detected field types.", + "lv_3dtexture": { + "_auto": { + "fields": { + "id": "int", + "h_flip": "bool", + "v_flip": "bool" + } + } + }, + "lv_animimg": { + "_auto": { + "fields": { + "anim": "int", + "dsc": "pointer", + "pic_count": "int" + } + }, + "summary_tpl": "", + "primary": [ + "pic_count" + ] + }, + "lv_arc": { + "_auto": { + "fields": { + "rotation": "int", + "indic_angle_start": "int", + "indic_angle_end": "int", + "bg_angle_start": "int", + "bg_angle_end": "int", + "value": "int", + "min_value": "int", + "max_value": "int", + "dragging": "bool", + "type": "enum:2", + "min_close": "bool", + "in_out": "bool", + "chg_rate": "int", + "last_tick": "int", + "last_angle": "int", + "knob_offset": "int" + } + }, + "summary_tpl": "{value} [{min_value}..{max_value}]", + "primary": [ + "value", + "min_value", + "max_value", + "rotation", + "type" + ], + "enums": { + "type": [ + "NORMAL", + "SYMMETRIC", + "REVERSE" + ] + } + }, + "lv_arclabel": { + "_auto": { + "fields": { + "text": "string", + "dot_begin": "int", + "angle_start": "int", + "angle_size": "int", + "offset": "int", + "radius": "int", + "center_offset": "point", + "dir": "int", + "text_align_v": "int", + "text_align_h": "int", + "static_txt": "bool", + "recolor": "bool", + "overflow": "enum:2", + "end_overlap": "bool" + } + }, + "summary_tpl": "{text}", + "primary": [ + "text", + "angle_start", + "angle_size", + "radius", + "dir" + ], + "enums": { + "dir": [ + "CW", + "CCW" + ], + "overflow": [ + "VISIBLE", + "ELLIPSIS", + "CLIP" + ] + } + }, + "lv_bar": { + "_auto": { + "fields": { + "cur_value": "int", + "min_value": "int", + "max_value": "int", + "start_value": "int", + "indic_area": "area", + "val_reversed": "bool", + "cur_value_anim": "int", + "start_value_anim": "int", + "mode": "enum:3", + "orientation": "enum:3" + } + }, + "summary_tpl": "{cur_value}/{max_value}", + "primary": [ + "cur_value", + "min_value", + "max_value", + "start_value", + "mode" + ], + "enums": { + "mode": [ + "NORMAL", + "SYMMETRICAL", + "RANGE" + ], + "orientation": [ + "AUTO", + "HORIZONTAL", + "VERTICAL" + ] + } + }, + "lv_button": { + "_auto": { + "fields": {} + } + }, + "lv_buttonmatrix": { + "_auto": { + "fields": { + "map_p": "string", + "button_areas": "pointer", + "ctrl_bits": "pointer", + "btn_cnt": "int", + "row_cnt": "int", + "btn_id_sel": "int", + "one_check": "bool", + "auto_free_map": "bool" + } + }, + "summary_tpl": "{btn_cnt} btns", + "primary": [ + "btn_cnt", + "row_cnt", + "btn_id_sel", + "one_check" + ] + }, + "lv_calendar": { + "_auto": { + "fields": { + "btnm": "pointer", + "today": "int", + "showed_date": "int", + "highlighted_dates": "pointer", + "highlighted_dates_num": "int", + "use_chinese_calendar": "bool" + } + }, + "summary_tpl": "", + "primary": [ + "today", + "showed_date", + "use_chinese_calendar" + ] + }, + "lv_canvas": { + "_auto": { + "fields": { + "draw_buf": "pointer", + "static_buf": "int" + } + }, + "summary_tpl": "", + "primary": [ + "draw_buf" + ] + }, + "lv_chart": { + "_auto": { + "fields": { + "series_ll": "int", + "cursor_ll": "int", + "pressed_point_id": "int", + "hdiv_cnt": "int", + "vdiv_cnt": "int", + "point_cnt": "int", + "type": "enum:4", + "update_mode": "enum:2" + } + }, + "summary_tpl": "{point_cnt}pts", + "primary": [ + "point_cnt", + "hdiv_cnt", + "vdiv_cnt", + "type" + ], + "enums": { + "type": [ + "NONE", + "LINE", + "BAR", + "SCATTER" + ], + "update_mode": [ + "SHIFT", + "CIRCULAR" + ] + } + }, + "lv_checkbox": { + "_auto": { + "fields": { + "txt": "string", + "static_txt": "bool" + } + }, + "summary_tpl": "{txt}", + "primary": [ + "txt", + "static_txt" + ] + }, + "lv_dropdown": { + "_auto": { + "fields": { + "list": "pointer", + "text": "string", + "symbol": "pointer", + "options": "string", + "option_cnt": "int", + "sel_opt_id": "int", + "sel_opt_id_orig": "int", + "pr_opt_id": "int", + "dir": "enum:4", + "static_options": "bool", + "selected_highlight": "bool", + "static_text": "bool" + } + }, + "summary_tpl": "sel={sel_opt_id}/{option_cnt}", + "primary": [ + "options", + "option_cnt", + "sel_opt_id", + "dir" + ], + "enums": { + "dir": [ + "TOP", + "BOTTOM", + "LEFT", + "RIGHT" + ] + } + }, + "lv_dropdown_list": { + "_auto": { + "fields": { + "dropdown": "pointer" + } + } + }, + "lv_image": { + "_auto": { + "fields": { + "src": "pointer", + "bitmap_mask_src": "pointer", + "offset": "point", + "w": "int", + "h": "int", + "rotation": "int", + "scale_x": "int", + "scale_y": "int", + "pivot": "point", + "src_type": "enum:2", + "cf": "enum:5", + "antialias": "bool", + "align": "enum:4", + "blend_mode": "enum:4" + } + }, + "summary_tpl": "{w}×{h}", + "primary": [ + "src", + "w", + "h", + "rotation", + "scale_x", + "scale_y", + "align" + ], + "enums": { + "src_type": [ + "VARIABLE", + "FILE", + "SYMBOL" + ], + "align": [ + "DEFAULT", + "TOP_LEFT", + "TOP_MID", + "TOP_RIGHT", + "BOTTOM_LEFT", + "BOTTOM_MID", + "BOTTOM_RIGHT", + "LEFT_MID", + "RIGHT_MID", + "CENTER", + "STRETCH", + "TILE" + ], + "blend_mode": [ + "NORMAL", + "ADDITIVE", + "SUBTRACTIVE", + "MULTIPLY" + ] + } + }, + "lv_imagebutton": { + "_auto": { + "fields": {} + } + }, + "lv_ime_pinyin": { + "_auto": { + "fields": { + "kb": "pointer", + "cand_panel": "pointer", + "dict": "pointer", + "k9_legal_py_ll": "int", + "cand_str": "string", + "k9_py_ll_pos": "int", + "k9_legal_py_count": "int", + "k9_input_str_len": "int", + "ta_count": "int", + "cand_num": "int", + "py_page": "int", + "mode": "int" + } + } + }, + "lv_keyboard": { + "_auto": { + "fields": { + "ta": "pointer", + "mode": "int", + "popovers": "bool" + } + }, + "summary_tpl": "mode={mode}", + "primary": [ + "ta", + "mode", + "popovers" + ], + "enums": { + "mode": [ + "TEXT_LOWER", + "TEXT_UPPER", + "SPECIAL", + "NUMBER", + "USER_1", + "USER_2", + "USER_3", + "USER_4" + ] + } + }, + "lv_label": { + "_auto": { + "fields": { + "text": "string", + "translation_tag": "string", + "dot_begin": "int", + "hint": "int", + "sel_start": "int", + "sel_end": "int", + "size_cache": "point", + "offset": "point", + "long_mode": "enum:4", + "static_txt": "bool", + "recolor": "bool", + "expand": "bool", + "invalid_size_cache": "bool", + "need_refr_text": "bool", + "text_size": "point" + } + }, + "summary_tpl": "{text}", + "primary": [ + "text", + "long_mode", + "recolor", + "static_txt" + ], + "enums": { + "long_mode": [ + "WRAP", + "DOT", + "SCROLL", + "CLIP", + "SCROLL_CIRCULAR" + ] + } + }, + "lv_led": { + "_auto": { + "fields": { + "color": "color", + "bright": "int" + } + }, + "summary_tpl": "bright={bright}", + "primary": [ + "color", + "bright" + ] + }, + "lv_line": { + "_auto": { + "fields": { + "point_num": "int", + "y_inv": "bool", + "point_array_is_mutable": "bool" + } + } + }, + "lv_menu": { + "_auto": { + "fields": { + "storage": "pointer", + "main": "pointer", + "main_page": "pointer", + "main_header": "pointer", + "main_header_title": "pointer", + "sidebar": "pointer", + "sidebar_page": "pointer", + "sidebar_header": "pointer", + "sidebar_header_title": "pointer", + "selected_tab": "pointer", + "history_ll": "int", + "cur_depth": "int", + "prev_depth": "int", + "sidebar_generated": "bool", + "mode_header": "enum:3", + "mode_root_back_btn": "enum:2" + } + }, + "summary_tpl": "", + "primary": [ + "cur_depth", + "sidebar_generated" + ] + }, + "lv_menu_page": { + "_auto": { + "fields": { + "title": "string", + "static_title": "bool" + } + }, + "summary_tpl": "{title}", + "primary": [ + "title", + "static_title" + ] + }, + "lv_msgbox": { + "_auto": { + "fields": { + "header": "pointer", + "content": "pointer", + "footer": "pointer", + "title": "pointer" + } + }, + "summary_tpl": "", + "primary": [ + "header", + "content", + "footer", + "title" + ] + }, + "lv_roller": { + "_auto": { + "fields": { + "option_cnt": "int", + "sel_opt_id": "int", + "sel_opt_id_ori": "int", + "inf_page_cnt": "int", + "mode": "enum:2", + "moved": "bool" + } + }, + "summary_tpl": "sel={sel_opt_id}/{option_cnt}", + "primary": [ + "option_cnt", + "sel_opt_id", + "mode" + ], + "enums": { + "mode": [ + "NORMAL", + "INFINITE" + ] + } + }, + "lv_scale": { + "_auto": { + "fields": { + "section_ll": "int", + "txt_src": "string", + "mode": "int", + "range_min": "int", + "range_max": "int", + "total_tick_count": "enum:15", + "major_tick_every": "enum:15", + "label_enabled": "bool", + "post_draw": "bool", + "draw_ticks_on_top": "bool", + "angle_range": "int", + "rotation": "int", + "custom_label_cnt": "int", + "last_tick_width": "int", + "first_tick_width": "int", + "needles": "int" + } + }, + "summary_tpl": "{range_min}..{range_max}", + "primary": [ + "mode", + "range_min", + "range_max", + "total_tick_count", + "angle_range", + "rotation" + ], + "enums": { + "mode": [ + "HORIZONTAL_TOP", + "HORIZONTAL_BOTTOM", + "VERTICAL_LEFT", + "VERTICAL_RIGHT", + "ROUND_INNER", + "ROUND_OUTER" + ] + } + }, + "lv_slider": { + "_auto": { + "fields": { + "left_knob_area": "area", + "right_knob_area": "area", + "pressed_point": "point", + "value_to_set": "pointer", + "dragging": "bool", + "left_knob_focus": "bool" + } + }, + "summary_tpl": "{cur_value}/{max_value}", + "primary": [ + "cur_value", + "min_value", + "max_value", + "mode", + "dragging" + ] + }, + "lv_spangroup": { + "_auto": { + "fields": { + "lines": "int", + "indent": "int", + "cache_w": "int", + "cache_h": "int", + "child_ll": "int", + "overflow": "bool", + "refresh": "bool" + } + }, + "summary_tpl": "", + "primary": [ + "lines", + "indent" + ] + }, + "lv_spinbox": { + "_auto": { + "fields": { + "value": "int", + "range_max": "int", + "range_min": "int", + "step": "int", + "digit_count": "enum:4", + "dec_point_pos": "enum:4", + "rollover": "bool", + "digit_step_dir": "enum:2" + } + }, + "summary_tpl": "{value}", + "primary": [ + "value", + "range_min", + "range_max", + "step", + "digit_count", + "dec_point_pos" + ] + }, + "lv_spinner": { + "_auto": { + "fields": { + "duration": "int", + "angle": "int" + } + }, + "summary_tpl": "{duration}ms", + "primary": [ + "duration", + "angle" + ] + }, + "lv_switch": { + "_auto": { + "fields": { + "anim_state": "int", + "orientation": "enum:3" + } + }, + "summary_tpl": "", + "primary": [ + "anim_state", + "orientation" + ], + "enums": { + "orientation": [ + "AUTO", + "HORIZONTAL", + "VERTICAL" + ] + } + }, + "lv_table": { + "_auto": { + "fields": { + "col_cnt": "int", + "row_cnt": "int", + "cell_data": "pointer", + "row_h": "pointer", + "col_w": "pointer", + "col_act": "int", + "row_act": "int" + } + }, + "summary_tpl": "{col_cnt}×{row_cnt}", + "primary": [ + "col_cnt", + "row_cnt", + "col_act", + "row_act" + ] + }, + "lv_tabview": { + "_auto": { + "fields": { + "tab_cur": "int", + "tab_pos": "int", + "tab_bar_size": "int" + } + }, + "summary_tpl": "tab={tab_cur}", + "primary": [ + "tab_cur", + "tab_pos", + "tab_bar_size" + ] + }, + "lv_textarea": { + "_auto": { + "fields": { + "label": "pointer", + "placeholder_txt": "string", + "placeholder_txt_size": "point", + "pwd_tmp": "string", + "pwd_bullet": "string", + "accepted_chars": "string", + "max_length": "int", + "pwd_show_time": "int", + "sel_start": "int", + "sel_end": "int", + "text_sel_in_prog": "bool", + "text_sel_en": "bool", + "pwd_mode": "bool", + "one_line": "bool", + "static_accepted_chars": "bool" + } + }, + "summary_tpl": "{placeholder_txt}", + "primary": [ + "placeholder_txt", + "max_length", + "pwd_show_time", + "accepted_chars" + ] + }, + "lv_tileview": { + "_auto": { + "fields": { + "tile_act": "pointer" + } + }, + "summary_tpl": "", + "primary": [ + "tile_act" + ] + }, + "lv_tileview_tile": { + "_auto": { + "fields": { + "dir": "int" + } + }, + "summary_tpl": "", + "primary": [ + "dir" + ] + }, + "lv_win": { + "_auto": { + "fields": {} + } + } +}