- UI defined in player.json (Bricks JSON schema) - Custom widgets: VideoPlayer (AVPlayer layer), ProgressSlider (seek bar) - PlayerBridge connects AVPlayer to BricksEngine event bus - All interactions via binds/events (no imperative UI code) - Depends on SwiftBricks SPM package
110 lines
5.2 KiB
JSON
110 lines
5.2 KiB
JSON
{
|
||
"id": "app",
|
||
"widgettype": "VBox",
|
||
"options": { "width": "100%", "height": "100%", "spacing": 0, "padding": "0" },
|
||
"subwidgets": [
|
||
{
|
||
"widgettype": "VideoPlayer",
|
||
"id": "video_player",
|
||
"options": { "width": "100%", "bgcolor": "#000000" },
|
||
"binds": [
|
||
{ "wid": "self", "event": "click", "actiontype": "event", "target": "player.toggle" }
|
||
]
|
||
},
|
||
{
|
||
"widgettype": "VBox",
|
||
"options": { "width": "100%", "css": "card", "padding": "8px", "spacing": 4 },
|
||
"subwidgets": [
|
||
{
|
||
"widgettype": "HBox",
|
||
"options": { "width": "100%", "spacing": 6, "alignItems": "center" },
|
||
"subwidgets": [
|
||
{ "widgettype": "Text", "id": "time_current", "options": { "text": "00:00", "i18n": false } },
|
||
{ "widgettype": "ProgressSlider", "id": "progress_slider", "options": { "width": "100%" } },
|
||
{ "widgettype": "Text", "id": "time_total", "options": { "text": "00:00", "i18n": false } }
|
||
]
|
||
},
|
||
{
|
||
"widgettype": "HBox",
|
||
"options": { "width": "100%", "spacing": 4, "alignItems": "center" },
|
||
"subwidgets": [
|
||
{
|
||
"widgettype": "Button", "id": "btn_prev",
|
||
"options": { "label": "⏮", "css": "text" },
|
||
"binds": [{ "wid": "self", "event": "click", "actiontype": "event", "target": "player.prev" }]
|
||
},
|
||
{
|
||
"widgettype": "Button", "id": "btn_play",
|
||
"options": { "label": "▶️", "css": "text" },
|
||
"binds": [{ "wid": "self", "event": "click", "actiontype": "event", "target": "player.toggle" }]
|
||
},
|
||
{
|
||
"widgettype": "Button", "id": "btn_next",
|
||
"options": { "label": "⏭", "css": "text" },
|
||
"binds": [{ "wid": "self", "event": "click", "actiontype": "event", "target": "player.next" }]
|
||
},
|
||
{ "widgettype": "Filler" },
|
||
{
|
||
"widgettype": "Text", "id": "track_label",
|
||
"options": { "text": "🎵 Track 1", "i18n": false }
|
||
},
|
||
{
|
||
"widgettype": "Button", "id": "btn_track",
|
||
"options": { "label": "音轨", "css": "text" },
|
||
"binds": [{ "wid": "self", "event": "click", "actiontype": "event", "target": "player.show_tracks" }]
|
||
},
|
||
{
|
||
"widgettype": "Button", "id": "btn_repeat",
|
||
"options": { "label": "🔁 列表循环", "css": "text" },
|
||
"binds": [{ "wid": "self", "event": "click", "actiontype": "event", "target": "player.cycle_repeat" }]
|
||
},
|
||
{
|
||
"widgettype": "Button", "id": "btn_fullscreen",
|
||
"options": { "label": "⛶", "css": "text" },
|
||
"binds": [{ "wid": "self", "event": "click", "actiontype": "event", "target": "player.fullscreen" }]
|
||
}
|
||
]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"widgettype": "HBox",
|
||
"options": { "width": "100%", "padding": "4px", "spacing": 6, "alignItems": "center" },
|
||
"subwidgets": [
|
||
{
|
||
"widgettype": "InlineForm",
|
||
"id": "add_form",
|
||
"options": {
|
||
"show_label": false,
|
||
"submit_label": "添加URL",
|
||
"fields": [
|
||
{ "name": "url", "placeholder": "M3U8 / 视频URL", "uitype": "str", "cwidth": 30 }
|
||
]
|
||
},
|
||
"binds": [{ "wid": "self", "event": "submit", "actiontype": "event", "target": "player.add_url" }]
|
||
},
|
||
{
|
||
"widgettype": "Button", "id": "btn_open_file",
|
||
"options": { "label": "📂 打开文件", "css": "text" },
|
||
"binds": [{ "wid": "self", "event": "click", "actiontype": "event", "target": "player.open_file" }]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"widgettype": "VScrollPanel",
|
||
"id": "playlist_panel",
|
||
"options": { "width": "100%", "css": "filler" },
|
||
"subwidgets": [
|
||
{
|
||
"widgettype": "Title5",
|
||
"options": { "text": "播放列表", "i18n": true }
|
||
},
|
||
{
|
||
"widgettype": "Text", "id": "playlist_empty",
|
||
"options": { "text": "暂无媒体,请添加文件或URL", "i18n": true, "color": "#888888" }
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|