@shijiu/jsview-vue 0.9.359-alpha.0 → 0.9.422

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dom/bin/jsview-browser-debug-dom.min.js +1 -1
  2. package/dom/bin/jsview-dom.min.js +1 -1
  3. package/dom/target_core_revision.js +3 -3
  4. package/loader/jsview.config.default.js +1 -1
  5. package/loader/jsview.default.config.js +1 -1
  6. package/loader/loader.js +2 -1
  7. package/package.json +1 -1
  8. package/patches/node_modules/@vue/compiler-sfc/dist/jsview-style-format.js +1 -1
  9. package/patches/node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js +20 -7
  10. package/samples/Basic/App.vue +37 -29
  11. package/samples/Basic/components/ContentBlock.vue +1 -1
  12. package/samples/Basic/components/FontStyle.css +6 -0
  13. package/samples/Basic/components/div/DivBackground.vue +1 -1
  14. package/samples/Basic/components/div/DivCssScoped.vue +6 -13
  15. package/samples/Basic/components/div/DivCssVar.vue +3 -7
  16. package/samples/Basic/components/div/DivGroup2.vue +7 -2
  17. package/samples/Basic/components/div/DivLayout.vue +3 -3
  18. package/samples/Basic/components/div/DivTransform.vue +27 -0
  19. package/samples/Basic/components/img/ImageGroup.vue +31 -0
  20. package/samples/Basic/components/img/ImgLayout.vue +41 -0
  21. package/samples/Basic/components/panel/Panel1.vue +53 -0
  22. package/samples/Basic/components/panel/Panel2.vue +35 -0
  23. package/samples/Basic/components/panel/TitleBar.vue +29 -0
  24. package/samples/Basic/components/text/TextEmoji.vue +30 -0
  25. package/samples/Basic/components/text/{TextGroup.vue → TextGroup1.vue} +0 -0
  26. package/samples/Basic/components/text/TextGroup2.vue +31 -0
  27. package/utils/JsViewEngineWidget/JsvFocusBlock.vue +13 -0
  28. package/utils/JsViewEngineWidget/MetroWidget/ContentView.vue +63 -0
  29. package/utils/JsViewEngineWidget/MetroWidget/Dispatcher.js +19 -0
  30. package/utils/JsViewEngineWidget/MetroWidget/DivWrapper.vue +51 -0
  31. package/utils/JsViewEngineWidget/MetroWidget/ItemView.vue +220 -0
  32. package/utils/JsViewEngineWidget/MetroWidget/MetroWidget.vue +1860 -0
  33. package/utils/JsViewEngineWidget/MetroWidget/RootView.vue +153 -0
  34. package/utils/JsViewEngineWidget/SimpleWidget/SimpleWidget.vue +137 -118
  35. package/utils/JsViewEngineWidget/TemplateParser.js +212 -158
  36. package/utils/JsViewEngineWidget/index.js +3 -2
  37. package/utils/JsViewPlugin/BrowserPluginLoader.js +14 -0
  38. package/utils/JsViewPlugin/JsvPlayer/JsvMedia.js +502 -108
  39. package/utils/JsViewPlugin/JsvPlayer/JsvMediaBrowserInterface.js +101 -0
  40. package/utils/JsViewPlugin/JsvPlayer/JsvPlayer.vue +262 -92
  41. package/utils/JsViewPlugin/JsvPlayer/JsvPlayerBrowser.vue +40 -0
  42. package/utils/JsViewPlugin/JsvPlayer/index.js +27 -0
  43. package/utils/JsViewVueWidget/BrowserDebugWidget/BrowserTextureAnim.vue +1 -2
  44. package/utils/JsViewVueWidget/JsvMarquee.vue +18 -10
  45. package/utils/JsViewVueWidget/JsvMaskClipDiv.vue +3 -3
  46. package/utils/JsViewVueWidget/JsvNinePatch.vue +28 -10
  47. package/samples/Basic/components/TitleBar.vue +0 -27
@@ -0,0 +1,1860 @@
1
+ <!--
2
+ * @Author: ChenChanghua
3
+ * @Date: 2021-09-22 16:08:58
4
+ * @LastEditors: ChenChanghua
5
+ * @LastEditTime: 2022-03-25 18:08:58
6
+ * @Description: file content
7
+ -->
8
+
9
+ <!--
10
+ * 【界面概述】
11
+ * 展示SimpleWidget控件的用法
12
+ *
13
+ * 【控件介绍】
14
+ * SimpleWidget:
15
+ * props:
16
+ * top {int} 控件的y,默认为0
17
+ * left {int} 控件的x, 默认为0
18
+ * width {int} (必选)控件的宽
19
+ * height {int} (必选)控件的高
20
+ * layoutType {String} 布局的类型, 目前支持 "relative"(默认)|"absolute", relative为自动布局,
21
+ absolute为依据measures中给的top, left布局
22
+ *
23
+ * name {string} 用于设置焦点的名称
24
+ * padding {object} 控件内边距, 默认为{left: 0, right: 0, top: 0, bottom: 0}
25
+ * direction {enum} (必选)控件方向 HORIZONTAL/VERTICAL
26
+ * loopFocus {boolean} 焦点到边界后自动转到下一行,默认false
27
+ * initFocusId {int} 初始焦点,默认为0
28
+ * slideSetting {SlideSetting} 页面滑动的设置, 目前SlideSetting有三个子类, 具体见 WidgetCommon 中的说明
29
+ WholePageSlide
30
+ @description 整页滚动的设置类
31
+ @constructor {Object} {speed, easing, boundProtect}
32
+ SeamlessSlide
33
+ @description 平滑滚动的设置类
34
+ @constructor {Object} {startPercent, endPercent, speed, easing, boundProtect}
35
+ FixPositionSlide
36
+ @description 固定位置滚动的设置类
37
+ @constructor {Object} {fixPercent, speed, easing, boundProtect}
38
+ * itemConfig {Function (item) => {takeOverSlide: true}} 返回item的配置信息
39
+ * enableItemRenderBreak {boolean} 按键事件是否能打断item的描画, 默认为false
40
+ * placeHolderSetting {Object} item描画可打断时, 显示的占位符
41
+ {
42
+ backgroundColor: 纯色占位符
43
+ borderRadius: 占位符圆角设置
44
+ gap: 占位符之间的gap
45
+ }
46
+ * sendFocusRectEvent {boolean} item获焦时在焦点树上冒泡一个事件, MetroWidget接收到事件时会触发滚动操作, 一般用于嵌套
47
+ 时的滚动
48
+
49
+ * data {array} 数据列表
50
+ * provideData {function} 获取数据的回调, 与data二选一
51
+ * measures {function} (必选)返回item的模板信息的回调,
52
+ * @params item {object} data中的数据
53
+ * @return 模板信息,
54
+ {
55
+ left: 绝对布局时的left,
56
+ top: 绝对布局时的top,
57
+ width: item的宽,
58
+ height: item的高,
59
+ focusable: item是否可以获得焦点,
60
+ marginBottom: item的下方margin,
61
+ marginRight: item的右方margin,
62
+ findNextAnchor: 自定义上下左右四条边寻找临近item的基准位置,值为0-1 {left: 0.5, right: 0.5, top: 0.5, bottom: 0.5}
63
+ doSlide: item获得焦点时是否滚动, 一般用于嵌套的情景, 由内部组件控制滚动
64
+ }
65
+ *
66
+ * onFocus {function} 控件获取焦点的回调
67
+ * onBlur {function} 控件失去焦点的回调
68
+ * onEdge {function} 焦点移动到边缘时的回调
69
+ * @params {object} edgeInfo {
70
+ "direction": {EdgeDirection}边缘方向,
71
+ "rect": 到达边缘时的区域{x: 0, y: 0, width: 0,height: 0}
72
+ }
73
+ *
74
+ * enableTouch {boolean} 支持触控
75
+ * loadAll {boolean} 加载不显示的view,触控场景使用
76
+ * flingPageWidth {}
77
+ * flingPageEdge {}
78
+ * methods:
79
+ slideTo
80
+ @description 滚动到指定位置
81
+ @params {int} position 滚动目标位置
82
+ @params {boolean} doAnim 是否做动画
83
+ slideToItem
84
+ @description 滚动到指定item
85
+ @params {int} index 目标item的index, 注意与item的id区别, id是可获焦item的序号
86
+ @params {boolean} doAnim 是否做动画
87
+ setEnterFocusId
88
+ @description 设置落焦时的焦点
89
+ @params {int} id 落焦时的焦点id
90
+ setEnterFocusRect
91
+ @description 设置落焦时的焦点查找去区域
92
+ @params {Object} rectInfo 焦点移入时的区域,SimpleWidget 将会根据此区域寻找最近的item
93
+ {
94
+ direction: {EdgeDirection}
95
+ rect: {
96
+ x: {int},
97
+ y: {int},
98
+ width: {int},
99
+ height: {int}
100
+ }
101
+ }
102
+ setFocusId
103
+ @description 设置焦点
104
+ @params {int} id 焦点id
105
+ @params {boolean} needSlide 是否需要滚动
106
+ @params {boolean} doAnim 滚动时是否做动画
107
+ refreshData
108
+ @description 刷新数据, 只有存在provideData时才会生效
109
+ @params {boolean} forceUpdate 是否强制刷新
110
+ * 【技巧说明】
111
+ * Q: 插槽props如何使用?
112
+ * A: data: 当前item的数据
113
+ * query: 获取一些额外信息的对象
114
+ {
115
+ id: {int} item的id,
116
+ position: {Function} (index : int) => {left: 0, top: 0, width: 0, height: 0}
117
+ 获取 item 相对 SimpleWidget 左上角坐标
118
+ templatePosition: {Function} (index : int) => {left: 0, top: 0, width: 0, height: 0}
119
+ 获取 item 的布局坐标, 一般用于自定义滚动
120
+ absolutePosition: {Function} (index : int) => {left: 0, top: 0, width: 0, height: 0}
121
+ 获取 item 的绝对坐标, 一般用于控制焦点
122
+ getCurrentFocusId: {Function} () => {id: 0, index: 0}
123
+ 获取当前焦点信息,id 为在可获得焦点元素列表中的索引,index 为在所有元素列表中的索引
124
+ slideTo: {Function} (targetPosition : int, doAnim : Boolean) => void
125
+ 滚动到指定位置,注意这个位置是 item 的布局坐标,另外当某些item需要接管滚动时,itemConfig中takeOverSlide要为true。
126
+ }
127
+ * onEdge: 若单元格内另有可接管按键的控件(如SimpleWidget),该控件到达边缘需要通知SimpleWidget时的回调
128
+ * onAction: 单元格内控件需要通过onAction.register方法向SimpleWidget注册 item 的回调,回调函数有
129
+ onFocus, onBlur, onClick, onWidgetEdge
130
+ *
131
+ * Q: 如何进行布局,定制每个格的尺寸?
132
+ * A: 首先选定一个布局的方式,一列列地横向布局(HROIZONTAL)还是一行行地纵向(VERTICAL)布局,设置给属性direction
133
+ * 然后将单元格尺寸反馈器(函数)设置到measures中,进行布局时,组件会回调measures函数获得每个单元格的尺寸,
134
+ * 当一列放满单元格后(如果是纵向布局,则是一行放满后),自动换列去布局下一列。
135
+ *
136
+ * Q: 单元格的普通状态,焦点状态,失焦状态如何渲染?
137
+ * A: 在单元的空间中通过往onAction注册的onFocus,onBlur控制显示
138
+ *
139
+ * Q: 控件中的导航处理(上下左右,OK键)需要什么响应的开发?
140
+ * A: 上下左右键已经由控件接管,不需要开发者而外处理,上下左右键会触发翻页时间,翻页的形式由属性slideStyle来定制。
141
+ * 另外,通过OnClick属性可以注册按键回调函数,来处理用户的OK键动作。
142
+ *
143
+ * Q: 边缘格获取焦点放大后显示不全?
144
+ * A: 设置padding,注意item的排布范围是控件的宽高减去对应的padding
145
+ *
146
+ * Q: 焦点怎么移出控件?
147
+ * A: 当焦点移动到控件边缘时,会调用onEdge回调。在回调中通过参数传递的值来决定焦点转移的行为
148
+ *
149
+ * Q: item内是可获焦的元素时该如何处理?
150
+ * A: 焦点处理: item内的元素需要在注册的onFocus和onBlue回调中管理焦点. 即在onFocus时把焦点设置到内部的可获焦节点;
151
+ onBlur时将焦点给回父SimpleWidget, 注意此时是焦点树上的子节点往父节点切换, 此时为了把子节点blur掉,
152
+ requestFocus函数需要传递一个false参数
153
+ -->
154
+ <script>
155
+ import { ref, reactive } from "vue";
156
+ import RootView from "./RootView.vue";
157
+ import Forge from "../ForgeDefine";
158
+ import { TemplateParser } from "../TemplateParser";
159
+ import { SingleRangeModel } from "../RangeModel";
160
+ import Dispatcher from "./Dispatcher";
161
+ import {
162
+ EdgeDirection,
163
+ VERTICAL,
164
+ HORIZONTAL,
165
+ getPositionRelativeToView,
166
+ SlideSetting,
167
+ SeamlessSlide,
168
+ } from "../WidgetCommon";
169
+
170
+ let emptyFunc = () => {};
171
+ // randomColor() {
172
+ // let randomColor = Math.round(Math.random() * 2 ** 24).toString(16);
173
+ // return (
174
+ // "#" + new Array(6 - randomColor.length).fill("0").join("") + randomColor
175
+ // );
176
+ // },
177
+
178
+ class TemplateItemAdder {
179
+ constructor(template_parser, data, measure_func, page_range, side_effect) {
180
+ this._TemplateParser = template_parser;
181
+ this._Data = data;
182
+ this._PageRange = page_range;
183
+ this._MeasureFunc = measure_func;
184
+ this._CurIndex = 0;
185
+ this._CurPage = 0;
186
+ this._KeyIndex = 0; //分页处的item
187
+ this._SideEffect = side_effect;
188
+ this._Key =
189
+ this._TemplateParser._Template.Orient.type === HORIZONTAL
190
+ ? "xPos"
191
+ : "yPos";
192
+ }
193
+
194
+ tryAddItem(cur_item = null, page_num = 1) {
195
+ let added = false;
196
+ if (
197
+ this._CurIndex < this._Data.length &&
198
+ (!cur_item ||
199
+ cur_item[this._Key] >= (this._CurPage - 1) * this._PageRange)
200
+ ) {
201
+ while (this._CurIndex < this._Data.length) {
202
+ let item = this._TemplateParser.ParseTemplateItem(
203
+ this._MeasureFunc(this._Data[this._CurIndex]),
204
+ this._Data[this._CurIndex]
205
+ );
206
+ this._SideEffect?.(item);
207
+ this._CurIndex++;
208
+ if (item[this._Key] >= (this._CurPage + page_num) * this._PageRange) {
209
+ this._CurPage += page_num;
210
+ break;
211
+ }
212
+ }
213
+ this._TemplateParser.CalculateNeighborWhenAddStop();
214
+ added = true;
215
+ }
216
+ // 添加完成需要计算neighbor
217
+ return added;
218
+ }
219
+
220
+ tryAddItemByPosition(position) {
221
+ const page_num = Math.ceil(position / this._PageRange + 1);
222
+ if (page_num > this._CurPage) {
223
+ this.tryAddItem(null, page_num - this._CurPage);
224
+ }
225
+ }
226
+
227
+ tryAddItemByIndex(index) {
228
+ let added = false;
229
+ while (this._CurIndex <= index) {
230
+ added = true;
231
+ let item = this._TemplateParser.ParseTemplateItem(
232
+ this._MeasureFunc(this._Data[this._CurIndex]),
233
+ this._Data[this._CurIndex]
234
+ );
235
+ this._SideEffect?.(item);
236
+ this._CurIndex++;
237
+ if (item[this._Key] >= (this._CurPage + 1) * this._PageRange) {
238
+ this._CurPage++;
239
+ }
240
+ }
241
+ if (added) {
242
+ this._TemplateParser.CalculateNeighborWhenAddStop();
243
+ }
244
+ //再添加一屏
245
+ this.tryAddItem(this._TemplateParser.GetItem(index), 2);
246
+ }
247
+
248
+ tryAddItemById(id) {
249
+ const last_item = this._TemplateParser.GetItem(-1);
250
+ if (!last_item || last_item.id < id) {
251
+ let added = false;
252
+ let item = last_item;
253
+ while (!item || item.id < id) {
254
+ added = true;
255
+ item = this._TemplateParser.ParseTemplateItem(
256
+ this._MeasureFunc(this._Data[this._CurIndex]),
257
+ this._Data[this._CurIndex]
258
+ );
259
+ this._SideEffect?.(item);
260
+ if (item[this._Key] >= (this._CurPage + 1) * this._PageRange) {
261
+ this._CurPage++;
262
+ }
263
+ this._CurIndex++;
264
+ }
265
+ if (added) {
266
+ this._TemplateParser.CalculateNeighborWhenAddStop();
267
+ }
268
+ }
269
+ //再添加一屏
270
+ this.tryAddItem(this._TemplateParser.GetItemById(id), 2);
271
+ }
272
+
273
+ updateData(data) {
274
+ this._Data = data;
275
+ }
276
+ }
277
+
278
+ class ItemRange {
279
+ constructor(start, end) {
280
+ this.start = start;
281
+ this.end = end;
282
+ }
283
+
284
+ _merge(from, to) {
285
+ if (to.ifOverlap(from)) {
286
+ to.start = Math.min(from.start, to.start);
287
+ to.end = Math.max(from.end, to.end);
288
+ return true;
289
+ }
290
+ return false;
291
+ }
292
+
293
+ merge(from) {
294
+ let to = this.clone();
295
+ this._merge(from, to);
296
+ return to;
297
+ }
298
+
299
+ selfMerge(from) {
300
+ return this._merge(from, this);
301
+ }
302
+
303
+ ifOverlap(range) {
304
+ return this.start <= range.end && range.start <= this.end;
305
+ }
306
+
307
+ clone() {
308
+ return new ItemRange(this.start, this.end);
309
+ }
310
+ }
311
+
312
+ function mergeRangeList(list_1, list_2) {
313
+ const result_list = list_1.concat();
314
+ for (let list_2_item of list_2) {
315
+ let item = list_2_item.clone();
316
+ for (let i = result_list.length - 1; i >= 0; i--) {
317
+ if (item.selfMerge(result_list[i])) {
318
+ result_list.splice(i, 1);
319
+ }
320
+ }
321
+ result_list.push(item);
322
+ }
323
+ return result_list;
324
+ }
325
+
326
+ class PageUpdater {
327
+ constructor(update_func) {
328
+ this.tmpRangeList = [];
329
+ this.rangeList = [];
330
+
331
+ this._updateFunc = update_func;
332
+ }
333
+
334
+ update(template, visible_start, visible_end, anchor_id, merge_tmp) {
335
+ let visible_range = template.GetVisibleItemList(
336
+ visible_start,
337
+ visible_end,
338
+ anchor_id
339
+ );
340
+ let range = new ItemRange(
341
+ visible_range.visibleStart,
342
+ visible_range.visibleEnd
343
+ );
344
+
345
+ if (this.rangeList.length > 0) {
346
+ let pre_tmp = this.tmpRangeList;
347
+ if (range.ifOverlap(this.rangeList[0])) {
348
+ this.tmpRangeList = [range.merge(this.rangeList[0])];
349
+ } else {
350
+ this.tmpRangeList = [range, this.rangeList[0]];
351
+ }
352
+ if (merge_tmp) {
353
+ this.tmpRangeList = mergeRangeList(this.tmpRangeList, pre_tmp);
354
+ }
355
+ }
356
+
357
+ this.rangeList = [range];
358
+ }
359
+
360
+ debugPrint() {
361
+ return (
362
+ "list: " +
363
+ JSON.stringify(this.rangeList) +
364
+ ", tmplist: " +
365
+ JSON.stringify(this.tmpRangeList)
366
+ );
367
+ }
368
+
369
+ apply() {
370
+ this.tmpRangeList = [];
371
+ this._updateFunc(this.rangeList);
372
+ }
373
+
374
+ applyTmp() {
375
+ if (this.tmpRangeList.length > 0) {
376
+ this._updateFunc(this.tmpRangeList);
377
+ }
378
+ }
379
+
380
+ getRange() {
381
+ return this.rangeList[0];
382
+ }
383
+ }
384
+
385
+ const _getPadding = function (padding) {
386
+ let result = {
387
+ left: 0,
388
+ right: 0,
389
+ top: 0,
390
+ bottom: 0,
391
+ };
392
+
393
+ if (typeof padding !== "undefined" && padding !== null) {
394
+ Object.assign(result, padding);
395
+ }
396
+ return result;
397
+ };
398
+
399
+ const _getTemplateParser = function (
400
+ width,
401
+ height,
402
+ direction,
403
+ padding,
404
+ support_history_path,
405
+ layout_type
406
+ ) {
407
+ let fixed_padding = _getPadding(padding);
408
+ let page_size;
409
+ let line_max;
410
+ if (direction === VERTICAL) {
411
+ line_max = width - fixed_padding.left - fixed_padding.right;
412
+ page_size = height - fixed_padding.top - fixed_padding.bottom;
413
+ } else {
414
+ line_max = height - fixed_padding.top - fixed_padding.bottom;
415
+ page_size = width - fixed_padding.left - fixed_padding.right;
416
+ }
417
+ return new TemplateParser(
418
+ direction,
419
+ line_max,
420
+ page_size,
421
+ layout_type,
422
+ support_history_path
423
+ );
424
+ };
425
+
426
+ // direction key 是 vertical 的值
427
+ const directionFreeKeyMap = {
428
+ pos: {
429
+ true: "yPos",
430
+ false: "xPos",
431
+ },
432
+ size: {
433
+ true: "height",
434
+ false: "width",
435
+ },
436
+ neighbor: {
437
+ true: {
438
+ 1: "bottom",
439
+ "-1": "top",
440
+ },
441
+ false: {
442
+ 1: "bottom",
443
+ "-1": "top",
444
+ },
445
+ },
446
+ margin: {
447
+ true: "marginBottom",
448
+ false: "marginRight",
449
+ },
450
+ center: {
451
+ true: "centerYPos",
452
+ false: "centerXPos",
453
+ },
454
+ };
455
+
456
+ const defaultSlideSetting = new SeamlessSlide(0.2, 0.8);
457
+ const SimpleWidget = {
458
+ components: { RootView },
459
+ props: {
460
+ padding: {
461
+ type: Object,
462
+ default() {
463
+ return {
464
+ top: 0,
465
+ left: 0,
466
+ right: 0,
467
+ bottom: 0,
468
+ };
469
+ },
470
+ },
471
+ direction: {
472
+ type: Symbol,
473
+ default: HORIZONTAL,
474
+ },
475
+ height: {
476
+ type: Number,
477
+ required: true,
478
+ },
479
+ width: {
480
+ type: Number,
481
+ required: true,
482
+ },
483
+ slideBoundary: {
484
+ validator(value) {
485
+ return !isNaN(value) && value < 0.5;
486
+ },
487
+ default: 0.2,
488
+ },
489
+ onClick: {
490
+ type: Function,
491
+ default: emptyFunc,
492
+ },
493
+ loopFocus: {
494
+ type: Boolean,
495
+ default: false,
496
+ },
497
+ onEdge: {
498
+ type: Function,
499
+ default: emptyFunc,
500
+ },
501
+ onFocus: {
502
+ type: Function,
503
+ default: emptyFunc,
504
+ },
505
+ onBlur: {
506
+ type: Function,
507
+ default: emptyFunc,
508
+ },
509
+ loadAll: {
510
+ type: Boolean,
511
+ default: false,
512
+ },
513
+ enableTouch: {
514
+ type: Boolean,
515
+ default: false,
516
+ },
517
+ flingPageWidth: {
518
+ type: Number,
519
+ default: -1,
520
+ },
521
+ flingPageEdge: {
522
+ type: Number,
523
+ default: 1 / 4,
524
+ },
525
+ dispatcher: {
526
+ type: Object,
527
+ },
528
+ measures: {
529
+ type: Function,
530
+ required: true,
531
+ },
532
+ baseAnchor: {
533
+ type: Object,
534
+ },
535
+ initFocusId: {
536
+ type: Number,
537
+ default: 0,
538
+ },
539
+ data: {
540
+ type: Array,
541
+ },
542
+ left: {
543
+ type: Number,
544
+ default: 0,
545
+ },
546
+ top: {
547
+ type: Number,
548
+ default: 0,
549
+ },
550
+ name: {
551
+ type: String,
552
+ },
553
+ supportHistoryPath: {
554
+ type: Boolean,
555
+ default: true,
556
+ },
557
+ itemConfig: {
558
+ type: Function,
559
+ },
560
+ onFocusChange: {
561
+ type: Function,
562
+ },
563
+ slideSetting: {
564
+ type: SlideSetting,
565
+ default: defaultSlideSetting,
566
+ },
567
+ layoutType: {
568
+ type: String,
569
+ default: "relative",
570
+ },
571
+ enableItemRenderBreak: {
572
+ type: Boolean,
573
+ default: false,
574
+ },
575
+ provideData: {
576
+ type: Function,
577
+ },
578
+ placeHolderSetting: {
579
+ type: Object,
580
+ },
581
+ sendFocusRectEvent: {
582
+ type: Boolean,
583
+ default: false,
584
+ },
585
+ },
586
+ watch: {
587
+ data: function (value, oldValue) {
588
+ console.log("watch data change ", value, oldValue);
589
+ },
590
+ },
591
+ setup(props) {
592
+ return {
593
+ refreshToken: ref(0),
594
+ dataUpdateToken: 0,
595
+ templateParser: null,
596
+ innerData: [],
597
+ dataList: [],
598
+ pageUpdater: null,
599
+
600
+ enterFocusId: -1,
601
+ enterFocusRect: null,
602
+ focusId: 0,
603
+ preFocusId: -1,
604
+ preEdgeRect: null,
605
+ isFocus: false,
606
+ perAnchor: null,
607
+ rootElement: null,
608
+
609
+ visibleStart: 0,
610
+ visibleRange: 0,
611
+ visibleRangeWithPadding: 0,
612
+
613
+ touchContainerW: 0,
614
+ touchContainerH: 0,
615
+ touchListener: null,
616
+ innerPadding: null,
617
+ slidePile: null,
618
+ dragDirection: Forge.DragSetting.DIRECTION_HORIZONTAL,
619
+ vertical: props.direction == VERTICAL,
620
+ preAnchorItemIndex: -1,
621
+ templateItemAdder: null,
622
+ itemRender: ref(false),
623
+ };
624
+ },
625
+ methods: {
626
+ //供外部获取SimpleWidget内部状态
627
+ _injectHandler() {
628
+ return {
629
+ getPosition: this._getPosition,
630
+ getTemplatePosition: this._getTemplatePosition,
631
+ getAbsolutePosition: this._getAbsolutePosition,
632
+ registerItemFunc: this._registerItemFunc,
633
+ getCurrentFocusId: this._getCurrentId,
634
+ isFocus: this._isFocus,
635
+ customerSlide: this.slideTo,
636
+ updateMounted: this._updateMounted,
637
+ registerItemRef: this._registerItemRef,
638
+ onFocusChange: this._onFocusChange,
639
+ getPreEdgeRect: this._getPreEdgeRect,
640
+ sendCustomerEvent: this._bubbleCustomerEvent,
641
+ handler: this,
642
+ };
643
+ },
644
+
645
+ _getPreEdgeRect() {
646
+ return this.preEdgeRect;
647
+ },
648
+
649
+ _onFocusChange(id) {
650
+ this.onFocusChange?.(id);
651
+ },
652
+
653
+ _isFocus() {
654
+ return this.isFocus;
655
+ },
656
+
657
+ _getCurrentId() {
658
+ return {
659
+ id: this.focusId,
660
+ index: this.templateParser.FocusIdToIndex(this.focusId),
661
+ };
662
+ },
663
+
664
+ _registerItemFunc(index, handlerObj) {
665
+ this.innerData[index].itemHandler = handlerObj;
666
+ },
667
+
668
+ _registerItemRef(index, itemRef) {
669
+ this.innerData[index].itemRef = itemRef;
670
+ },
671
+
672
+ _updateMounted(index, mounted) {
673
+ if (index >= this.innerData.length) {
674
+ console.warn("sw updateMounted error: over range");
675
+ return;
676
+ }
677
+ this.innerData[index].mounted = mounted;
678
+ },
679
+
680
+ _getPosition(index) {
681
+ let item = this.templateParser.GetItem(index);
682
+ let x_offset = this.direction === VERTICAL ? 0 : -this.visibleStart;
683
+ let y_offset = this.direction === VERTICAL ? -this.visibleStart : 0;
684
+ return {
685
+ left: item.xPos + x_offset,
686
+ top: item.yPos + y_offset,
687
+ width: item.width,
688
+ height: item.height,
689
+ };
690
+ },
691
+
692
+ _getTemplatePosition(index) {
693
+ let item = this.templateParser.GetItem(index);
694
+ return {
695
+ left: item.xPos,
696
+ top: item.yPos,
697
+ width: item.width,
698
+ height: item.height,
699
+ };
700
+ },
701
+
702
+ _getAbsolutePosition(index, targetEle) {
703
+ return getPositionRelativeToView(
704
+ this.innerData[index].itemRef?.getDivElement(),
705
+ targetEle
706
+ );
707
+ },
708
+
709
+ _dispatchEvent(event) {
710
+ switch (event.type) {
711
+ case Dispatcher.Type.setEnterFocusId:
712
+ this.setEnterFocusId(event.data);
713
+ break;
714
+ case Dispatcher.Type.setEnterFocusRect:
715
+ this.setEnterFocusRect(event.data);
716
+ break;
717
+ case Dispatcher.Type.updateItem:
718
+ break;
719
+ case Dispatcher.Type.slideToItem:
720
+ if (event.data) {
721
+ this.slideToItem(event.data?.id, event.data?.doAnim);
722
+ }
723
+ break;
724
+ case Dispatcher.Type.setFocusId:
725
+ this.setFocusId(
726
+ event.data?.id,
727
+ event.data?.needSlide,
728
+ event.data?.doAnim
729
+ );
730
+ break;
731
+ default:
732
+ break;
733
+ }
734
+ },
735
+
736
+ setFocusId(id, needSlide = true, doAnim = false) {
737
+ this.templateItemAdder.tryAddItemById(id);
738
+ let next_focus_item = this.templateParser.GetItemById(id);
739
+ if (next_focus_item) {
740
+ if (needSlide) {
741
+ this.slideToItem(this.templateParser.FocusIdToIndex(id), doAnim);
742
+ }
743
+
744
+ const cur_focus_item = this.templateParser.GetItemById(this.focusId);
745
+ this.preFocusId = this.focusId;
746
+ this.focusId = next_focus_item.id;
747
+
748
+ let x_off_set = cur_focus_item.xPos - next_focus_item.xPos;
749
+ let y_off_set = cur_focus_item.yPos - next_focus_item.yPos;
750
+ this.preEdgeRect = {
751
+ direction: null,
752
+ rect: {
753
+ x: x_off_set,
754
+ y: y_off_set,
755
+ width: cur_focus_item.width,
756
+ height: cur_focus_item.height,
757
+ },
758
+ };
759
+ this._updateBlurItem();
760
+ this._updateFocusItem();
761
+ if (this.isFocus) {
762
+ this.innerData[
763
+ this.templateParser.FocusIdToIndex(this.preFocusId)
764
+ ].itemHandler.onBlur?.();
765
+ }
766
+ if (
767
+ this.isFocus &&
768
+ this.innerData[this.templateParser.FocusIdToIndex(this.focusId)]
769
+ .mounted
770
+ ) {
771
+ this.innerData[
772
+ this.templateParser.FocusIdToIndex(this.focusId)
773
+ ].itemHandler.onFocus?.(this.preEdgeRect);
774
+ }
775
+ }
776
+ },
777
+
778
+ setEnterFocusId(id) {
779
+ this.templateItemAdder.tryAddItemById(id);
780
+ this.enterFocusId = id;
781
+ },
782
+
783
+ setEnterFocusRect(rect) {
784
+ this.enterFocusRect = rect;
785
+ },
786
+
787
+ slideTo(position, doAnim) {
788
+ this.templateItemAdder.tryAddItemByPosition(position);
789
+ if (
790
+ typeof position !== "undefined" &&
791
+ position != null &&
792
+ this.visibleStart !== position
793
+ ) {
794
+ this.visibleStart = position;
795
+ let animObj = null;
796
+ if (doAnim) {
797
+ animObj = {
798
+ easing: this.slideSetting.Easing,
799
+ onstart: null,
800
+ speed: this.slideSetting.Speed,
801
+ onend: this._onSlideEnd,
802
+ };
803
+ }
804
+ this._slideTo(this.visibleStart, animObj);
805
+
806
+ this.pageUpdater.update(
807
+ this.templateParser,
808
+ this.visibleStart,
809
+ this.visibleStart + this.visibleRangeWithPadding - 1,
810
+ this.focusId
811
+ );
812
+ if (doAnim) {
813
+ this.pageUpdater.applyTmp();
814
+ } else {
815
+ this.pageUpdater.apply();
816
+ }
817
+ }
818
+ },
819
+
820
+ slideToItem(index, doAnim) {
821
+ this.templateItemAdder.tryAddItemByIndex(index);
822
+ const targetItem = this.templateParser.GetItem(index);
823
+ if (targetItem) {
824
+ const direction = index - this.preAnchorItemIndex > 0 ? 1 : -1;
825
+ const visible_start = this._calculateVisibleStart(
826
+ targetItem,
827
+ direction
828
+ );
829
+ this.slideTo(visible_start, doAnim);
830
+ }
831
+ },
832
+
833
+ refreshData(force_update) {
834
+ //由于data不支持reactive, 因此数据的更新只通过provideData
835
+ if (!this.provideData) {
836
+ console.error("refreshData: provideData is null.");
837
+ return;
838
+ }
839
+ let template_list = this.templateParser.GetTemplate().List;
840
+ let new_list = this.provideData();
841
+ if (this.focusId >= new_list.length) {
842
+ this.focusId = 0;
843
+ this.preFocusId = -1;
844
+ if (this.visibleStart !== 0) {
845
+ this.visibleStart = 0;
846
+ this._slideTo(0, null);
847
+ }
848
+ }
849
+ let new_index = 0;
850
+ if (!force_update) {
851
+ for (new_index = 0; new_index < new_list.length; ++new_index) {
852
+ let already_add = false;
853
+ for (let j = 0; j < template_list.length; ++j) {
854
+ if (new_list[new_index] === template_list[j].data) {
855
+ already_add = true;
856
+ break;
857
+ }
858
+ }
859
+ if (!already_add) {
860
+ break;
861
+ }
862
+ }
863
+ }
864
+ let need_update_content = false;
865
+ if (new_index === template_list.length) {
866
+ //原始数据都在
867
+ if (new_index !== new_list.length) {
868
+ //增加数据
869
+ need_update_content = true;
870
+ this.templateItemAdder.updateData(new_list);
871
+ } else {
872
+ //数据没变
873
+ }
874
+ } else {
875
+ //数据更改
876
+ need_update_content = true;
877
+ let template_parser = _getTemplateParser(
878
+ this.width,
879
+ this.height,
880
+ this.direction,
881
+ this.padding,
882
+ this.supportHistoryPath,
883
+ this.layoutType
884
+ );
885
+ this.innerData = [];
886
+ this.dataList = new_list;
887
+ this.templateItemAdder = new TemplateItemAdder(
888
+ template_parser,
889
+ new_list,
890
+ this.measures,
891
+ this.visibleRangeWithPadding,
892
+ this._onTemplateAdd
893
+ );
894
+ this.templateItemAdder.tryAddItemById(this.focusId);
895
+ this.templateParser = template_parser;
896
+ }
897
+ if (need_update_content) {
898
+ let template_list = this.templateParser.GetTemplate().List;
899
+ let last_item = template_list[template_list.length - 1];
900
+ this.touchListener = this._getTouchListener();
901
+ this.innerPadding = _getPadding(this.padding);
902
+ this.touchContainerW =
903
+ this.direction === VERTICAL
904
+ ? this.width
905
+ : last_item.xPos + last_item.width;
906
+ this.touchContainerH =
907
+ this.direction === VERTICAL
908
+ ? last_item.yPos + last_item.height
909
+ : this.height;
910
+ this.slidePile = new Forge.RectArea(
911
+ 0,
912
+ 0,
913
+ this.width - this.innerPadding.left - this.innerPadding.right,
914
+ this.height - this.innerPadding.top - this.innerPadding.bottom
915
+ );
916
+ this.dragDirection =
917
+ this.direction === VERTICAL
918
+ ? Forge.DragSetting.DIRECTION_VERTICAL
919
+ : Forge.DragSetting.DIRECTION_HORIZONTAL;
920
+ }
921
+ this.pageUpdater.update(
922
+ this.templateParser,
923
+ this.visibleStart,
924
+ this.visibleStart + this.visibleRangeWithPadding - 1,
925
+ this.focusId
926
+ );
927
+ this.dataUpdateToken++;
928
+ this.pageUpdater.apply();
929
+ if (
930
+ this.isFocus &&
931
+ this.innerData[this.templateParser.FocusIdToIndex(this.focusId)].mounted
932
+ ) {
933
+ this._updateFocusItem();
934
+ this.innerData[
935
+ this.templateParser.FocusIdToIndex(this.focusId)
936
+ ].itemHandler.onFocus?.(null);
937
+ }
938
+ },
939
+
940
+ _calculateNearestItemByRect(visible_range, enter_rect_info) {
941
+ let edge_direction = enter_rect_info.direction;
942
+ let rect = enter_rect_info.rect;
943
+ var direction = "";
944
+ var point = { x: 0, y: 0 };
945
+ switch (edge_direction) {
946
+ case EdgeDirection.left:
947
+ direction = "left";
948
+ point.x = rect.x;
949
+ point.y = rect.y + rect.height / 4;
950
+ break;
951
+ case EdgeDirection.right:
952
+ direction = "right";
953
+ point.x = rect.x + rect.width;
954
+ point.y = rect.y + rect.height / 4;
955
+ break;
956
+ case EdgeDirection.top:
957
+ direction = "up";
958
+ point.x = rect.x + rect.width / 4;
959
+ point.y = rect.y;
960
+ break;
961
+ case EdgeDirection.bottom:
962
+ direction = "down";
963
+ point.x = rect.x + rect.width / 4;
964
+ point.y = rect.y + rect.height;
965
+ break;
966
+ default:
967
+ break;
968
+ }
969
+
970
+ var src_x_range = new SingleRangeModel(point.x, point.x);
971
+ var src_y_range = new SingleRangeModel(point.y, point.y);
972
+
973
+ var min_distance_item = null;
974
+ var min_distance = -1;
975
+ var min_direction_weighted = 0;
976
+ var distance = 0;
977
+ var direction_weighted = 0; // 根据进入方向决定的权值,用于保证
978
+
979
+ let key_pos = this.direction === VERTICAL ? "yPos" : "xPos";
980
+ let key_width = this.direction === VERTICAL ? "height" : "width";
981
+ for (
982
+ let index = visible_range.start;
983
+ index <= visible_range.end;
984
+ index++
985
+ ) {
986
+ let item = this.templateParser.GetItem(index);
987
+ if (!item.focusable) continue;
988
+ let full_show =
989
+ item[key_pos] >= this.visibleStart &&
990
+ item[key_pos] + item[key_width] - 1 <=
991
+ this.visibleStart + this.visibleRange - 1;
992
+ if (full_show) {
993
+ let x_pos =
994
+ this.direction === VERTICAL
995
+ ? item.xPos
996
+ : item.xPos - this.visibleStart;
997
+ let y_pos =
998
+ this.direction === VERTICAL
999
+ ? item.yPos - this.visibleStart
1000
+ : item.yPos;
1001
+ var target_x_range = new SingleRangeModel(
1002
+ x_pos,
1003
+ x_pos + item.width - 1
1004
+ );
1005
+ var target_y_range = new SingleRangeModel(
1006
+ y_pos,
1007
+ y_pos + item.height - 1
1008
+ );
1009
+ switch (direction) {
1010
+ case "left":
1011
+ if (point.x < x_pos + item.width - 1) {
1012
+ // 这个点在item左侧,不可能左移到item
1013
+ continue;
1014
+ }
1015
+ if (point.y < y_pos) {
1016
+ //1.该点在其上,计算point和item右上角的距离
1017
+ distance =
1018
+ Math.pow(point.x - (x_pos + item.width - 1), 2) +
1019
+ Math.pow(y_pos - point.y, 2);
1020
+ } else if (src_y_range.IsInterAct(target_y_range)) {
1021
+ //2.该点在其中,计算point和item右边框的垂直距离
1022
+ distance = Math.pow(point.x - (x_pos + item.width - 1), 2);
1023
+ } else if (point.y > y_pos + item.height - 1) {
1024
+ //3.该点在其下,计算point到item右下角的距离
1025
+ distance =
1026
+ Math.pow(point.x - (x_pos + item.width - 1), 2) +
1027
+ Math.pow(point.y - (y_pos + item.height - 1), 2);
1028
+ }
1029
+ // point与item右边框的距离作为权值
1030
+ direction_weighted = Math.abs(point.x - (x_pos + item.width - 1));
1031
+ break;
1032
+ case "right":
1033
+ if (point.x > x_pos) {
1034
+ // 这个点在item右侧,不可能右移到item
1035
+ continue;
1036
+ }
1037
+ if (point.y < y_pos) {
1038
+ //1.该点在其上, 计算point和item左上角的距离
1039
+ distance =
1040
+ Math.pow(x_pos - point.x, 2) + Math.pow(y_pos - point.y, 2);
1041
+ } else if (src_y_range.IsInterAct(target_y_range)) {
1042
+ //2.该点在其中,计算point和item左边框的垂直距离
1043
+ distance = Math.pow(x_pos - point.x, 2);
1044
+ //(x2-x1)^2
1045
+ } else if (point.y > y_pos + item.height - 1) {
1046
+ //3.该点在其下,计算point和item左下角的距离
1047
+ distance =
1048
+ Math.pow(x_pos - point.x, 2) +
1049
+ Math.pow(point.y - (y_pos + item.height - 1), 2);
1050
+ }
1051
+ // point与item左边框的距离作为权值
1052
+ direction_weighted = Math.abs(point.x - x_pos);
1053
+ break;
1054
+ case "up":
1055
+ if (point.y < y_pos + item.height - 1) {
1056
+ // 这个点在item上侧,不可能上移到item
1057
+ continue;
1058
+ }
1059
+ if (point.x < x_pos) {
1060
+ //1.该点在其前,计算point和item右下角的距离
1061
+ distance =
1062
+ Math.pow(point.x - x_pos, 2) +
1063
+ Math.pow(point.y - (y_pos + item.height - 1), 2);
1064
+ } else if (src_x_range.IsInterAct(target_x_range)) {
1065
+ //2.该点在其中,计算point和item下边框的垂直距离
1066
+ distance = Math.pow(point.y - (y_pos + item.height - 1), 2);
1067
+ } else if (point.x > x_pos + item.width - 1) {
1068
+ //3.该点在其后,计算point和item左下角的距离
1069
+ distance =
1070
+ Math.pow(point.x - (x_pos + item.width - 1), 2) +
1071
+ Math.pow(point.y - (y_pos + item.height - 1), 2);
1072
+ }
1073
+ // point与item下边框的距离作为权值
1074
+ direction_weighted = Math.abs(
1075
+ point.y - (y_pos + item.height - 1)
1076
+ );
1077
+ break;
1078
+ case "down":
1079
+ if (point.y > y_pos) {
1080
+ // 这个点在item下侧,不可能下移到item
1081
+ continue;
1082
+ }
1083
+ if (point.x < x_pos) {
1084
+ //1.该点在其前,计算point和item右上角的距离
1085
+ distance =
1086
+ Math.pow(point.x - x_pos, 2) + Math.pow(y_pos - point.y, 2);
1087
+ } else if (src_x_range.IsInterAct(target_x_range)) {
1088
+ //2.该点在其中,计算point和item上边框的垂直距离
1089
+ distance = Math.pow(y_pos - point.y, 2);
1090
+ } else if (point.x > x_pos + item.width - 1) {
1091
+ //3.该点在其后,计算point和item左上角的距离
1092
+ distance =
1093
+ Math.pow(point.x - (x_pos + item.width - 1), 2) +
1094
+ Math.pow(y_pos - point.y, 2);
1095
+ }
1096
+ // point与item上边框的距离作为权值
1097
+ direction_weighted = Math.abs(point.y - y_pos);
1098
+ break;
1099
+ default:
1100
+ console.log("direction is error:" + direction);
1101
+ break;
1102
+ }
1103
+ if (min_distance < 0) {
1104
+ // 设置首个找到的项目
1105
+ min_distance = distance;
1106
+ min_direction_weighted = direction_weighted;
1107
+ min_distance_item = item;
1108
+ } else {
1109
+ if (
1110
+ min_direction_weighted > direction_weighted ||
1111
+ (min_direction_weighted === direction_weighted &&
1112
+ min_distance > distance)
1113
+ ) {
1114
+ // 先进行权值比较,在权值相等时,再比较距离
1115
+ min_distance = distance;
1116
+ min_direction_weighted = direction_weighted;
1117
+ min_distance_item = item;
1118
+ }
1119
+ }
1120
+ }
1121
+ }
1122
+
1123
+ if (min_distance_item) {
1124
+ return min_distance_item.id;
1125
+ } else {
1126
+ return 0;
1127
+ }
1128
+ },
1129
+
1130
+ _onClick(index) {
1131
+ return this.innerData[index].itemHandler.onClick?.();
1132
+ },
1133
+
1134
+ onKeyDown(ev) {
1135
+ let horizontal_direction = 0;
1136
+ let vertical_direction = 0;
1137
+ switch (ev.keyCode) {
1138
+ case 37:
1139
+ horizontal_direction = -1;
1140
+ this._moveToNext(horizontal_direction, vertical_direction);
1141
+ break;
1142
+ case 38:
1143
+ vertical_direction = -1;
1144
+ this._moveToNext(horizontal_direction, vertical_direction);
1145
+ break;
1146
+ case 39:
1147
+ horizontal_direction = 1;
1148
+ this._moveToNext(horizontal_direction, vertical_direction);
1149
+ break;
1150
+ case 40:
1151
+ vertical_direction = 1;
1152
+ this._moveToNext(horizontal_direction, vertical_direction);
1153
+ break;
1154
+ case 13:
1155
+ this._onClick(this.templateParser.FocusIdToIndex(this.focusId));
1156
+ break;
1157
+ default:
1158
+ //只接受上下左右确定键
1159
+ return false;
1160
+ }
1161
+ return true;
1162
+ },
1163
+
1164
+ _slideTo(target, animObj) {
1165
+ if (this.direction === HORIZONTAL) {
1166
+ this.rootElement.updatePosition(-target, 0, animObj);
1167
+ } else {
1168
+ this.rootElement.updatePosition(0, -target, animObj);
1169
+ }
1170
+ },
1171
+
1172
+ _onTemplateAdd(item) {
1173
+ this.innerData.push({
1174
+ data: item,
1175
+ index: this.innerData.length,
1176
+ controller: reactive({
1177
+ // visible: false,
1178
+ zIndex: 0,
1179
+ }),
1180
+ mounted: false,
1181
+ itemRef: null,
1182
+ itemHandler: {},
1183
+ itemConfig: this.itemConfig?.(item.data),
1184
+ });
1185
+ },
1186
+
1187
+ _onCustomerEvent(ev) {
1188
+ if (ev.type === "focusRect") {
1189
+ if (ev.ownerNode == this) {
1190
+ return false;
1191
+ }
1192
+ let item_div = ev.element;
1193
+ let direction = 0;
1194
+ if (this.direction == HORIZONTAL) {
1195
+ if (ev.direction == "left") {
1196
+ direction = -1;
1197
+ } else if (ev.direction == "right") {
1198
+ direction = 1;
1199
+ }
1200
+ } else {
1201
+ if (ev.direction == "top") {
1202
+ direction = -1;
1203
+ } else if (ev.direction == "bottom") {
1204
+ direction = 1;
1205
+ }
1206
+ }
1207
+ let item_layout = getPositionRelativeToView(
1208
+ item_div,
1209
+ this.$refs.rootView.getLocateDiv()
1210
+ );
1211
+ let obj = {
1212
+ xPos: item_layout.left,
1213
+ yPos: item_layout.top,
1214
+ width: item_layout.width,
1215
+ height: item_layout.height,
1216
+ index: this.templateParser.FocusIdToIndex(this.focusId),
1217
+ };
1218
+ let cur_slide = this._calculateVisibleStart(obj, direction);
1219
+ if (cur_slide != this.visibleStart) {
1220
+ this.visibleStart = cur_slide;
1221
+ this.pageUpdater.update(
1222
+ this.templateParser,
1223
+ this.visibleStart,
1224
+ this.visibleStart + this.visibleRangeWithPadding - 1,
1225
+ this.focusId,
1226
+ true
1227
+ );
1228
+ this.pageUpdater.applyTmp();
1229
+ this._slideTo(this.visibleStart, {
1230
+ easing: "",
1231
+ onstart: null,
1232
+ speed: this.slideSetting.Speed,
1233
+ onend: this._onSlideEnd,
1234
+ });
1235
+
1236
+ return true;
1237
+ }
1238
+ }
1239
+ return false;
1240
+ },
1241
+
1242
+ _bubbleCustomerEvent(event) {
1243
+ if (this.sendFocusRectEvent && event) {
1244
+ event.ownerNode = this;
1245
+ this.$refs.focusNode?.bubbleCustomerEvent(event);
1246
+ }
1247
+ },
1248
+
1249
+ _moveToNext(horizontal_direction, vertical_direction, item_edge_rect) {
1250
+ let cur_focus_item = this.templateParser.GetItemById(this.focusId);
1251
+ let next_item_id = this.templateParser.GetNextItem(
1252
+ this.focusId,
1253
+ vertical_direction,
1254
+ horizontal_direction,
1255
+ this.loopFocus
1256
+ );
1257
+ if (next_item_id >= 0) {
1258
+ this.preFocusId = this.focusId;
1259
+ let next_focus_item = this.templateParser.GetItemById(next_item_id);
1260
+ this.templateItemAdder.tryAddItem(next_focus_item, 1);
1261
+ this.focusId = next_focus_item.id;
1262
+
1263
+ let direction =
1264
+ this.direction === VERTICAL
1265
+ ? vertical_direction
1266
+ : horizontal_direction;
1267
+
1268
+ let cur_visible_start = this._calculateVisibleStart(
1269
+ next_focus_item,
1270
+ direction
1271
+ );
1272
+ if (
1273
+ !this.innerData[this.templateParser.FocusIdToIndex(next_item_id)]
1274
+ .itemConfig?.takeOverSlide &&
1275
+ this.visibleStart !== cur_visible_start
1276
+ ) {
1277
+ if (next_focus_item.doSlide) {
1278
+ this.visibleStart = cur_visible_start;
1279
+ let animObj = {
1280
+ easing: "",
1281
+ onstart: null,
1282
+ speed: this.slideSetting.Speed,
1283
+ onend: this._onSlideEnd,
1284
+ };
1285
+ this._slideTo(this.visibleStart, animObj);
1286
+ }
1287
+ this.pageUpdater.update(
1288
+ this.templateParser,
1289
+ cur_visible_start,
1290
+ cur_visible_start + this.visibleRangeWithPadding - 1,
1291
+ next_item_id
1292
+ );
1293
+ this.pageUpdater.applyTmp();
1294
+ }
1295
+
1296
+ let rect;
1297
+ let x_off_set = cur_focus_item.xPos - next_focus_item.xPos;
1298
+ let y_off_set = cur_focus_item.yPos - next_focus_item.yPos;
1299
+ if (item_edge_rect) {
1300
+ item_edge_rect.rect.x += x_off_set;
1301
+ item_edge_rect.rect.y += y_off_set;
1302
+ rect = item_edge_rect;
1303
+ } else {
1304
+ let direction;
1305
+ if (horizontal_direction > 0) {
1306
+ direction = EdgeDirection.right;
1307
+ } else if (horizontal_direction < 0) {
1308
+ direction = EdgeDirection.left;
1309
+ } else if (vertical_direction > 0) {
1310
+ direction = EdgeDirection.bottom;
1311
+ } else if (vertical_direction < 0) {
1312
+ direction = EdgeDirection.top;
1313
+ }
1314
+ rect = {
1315
+ direction,
1316
+ rect: {
1317
+ x: x_off_set,
1318
+ y: y_off_set,
1319
+ width: cur_focus_item.width,
1320
+ height: cur_focus_item.height,
1321
+ },
1322
+ };
1323
+ }
1324
+ this.preEdgeRect = rect;
1325
+ this._updateBlurItem();
1326
+ this._updateFocusItem();
1327
+ if (this.isFocus) {
1328
+ this.innerData[
1329
+ this.templateParser.FocusIdToIndex(this.preFocusId)
1330
+ ].itemHandler.onBlur?.();
1331
+ }
1332
+ if (
1333
+ this.isFocus &&
1334
+ this.innerData[this.templateParser.FocusIdToIndex(this.focusId)]
1335
+ .mounted
1336
+ ) {
1337
+ this.innerData[
1338
+ this.templateParser.FocusIdToIndex(this.focusId)
1339
+ ].itemHandler.onFocus?.(this.preEdgeRect);
1340
+ }
1341
+ } else {
1342
+ let x_off_set = this.direction === VERTICAL ? 0 : this.visibleStart;
1343
+ let y_off_set = this.direction === VERTICAL ? this.visibleStart : 0;
1344
+ let edge;
1345
+ if (horizontal_direction === 1) {
1346
+ edge = EdgeDirection.right;
1347
+ }
1348
+ if (horizontal_direction === -1) {
1349
+ edge = EdgeDirection.left;
1350
+ }
1351
+ if (vertical_direction === 1) {
1352
+ edge = EdgeDirection.bottom;
1353
+ }
1354
+ if (vertical_direction === -1) {
1355
+ edge = EdgeDirection.top;
1356
+ }
1357
+ let rect = {
1358
+ x: cur_focus_item.xPos - x_off_set,
1359
+ y: cur_focus_item.yPos - y_off_set,
1360
+ width: cur_focus_item.width,
1361
+ height: cur_focus_item.height,
1362
+ };
1363
+ if (this.onEdge) {
1364
+ this.onEdge({ direction: edge, rect: rect });
1365
+ }
1366
+ this.innerData[
1367
+ this.templateParser.FocusIdToIndex(this.focusId)
1368
+ ].itemHandler.reachEdge?.({
1369
+ direction: edge,
1370
+ });
1371
+ }
1372
+ },
1373
+
1374
+ _adjustToValidPosition(visible_start, adjust_end) {
1375
+ //TODO 优化
1376
+ let last_item = this.templateParser.GetItem(-1);
1377
+ let pos_key = directionFreeKeyMap.pos[this.direction === VERTICAL];
1378
+ let size_key = directionFreeKeyMap.size[this.direction === VERTICAL];
1379
+ let padding = _getPadding(this.padding);
1380
+
1381
+ let padding_offset =
1382
+ this.direction === VERTICAL
1383
+ ? padding["bottom"] + padding["top"]
1384
+ : padding["right"] + padding["left"];
1385
+ let visible_range =
1386
+ (this.direction === VERTICAL ? this.height : this.width) -
1387
+ padding_offset;
1388
+
1389
+ let last_position = last_item[pos_key] + last_item[size_key];
1390
+ let last_item_off_set = last_position - visible_range;
1391
+ last_item_off_set = last_item_off_set < 0 ? 0 : last_item_off_set;
1392
+ if (adjust_end && visible_start > last_item_off_set) {
1393
+ return last_item_off_set;
1394
+ } else if (visible_start < 0) {
1395
+ return 0;
1396
+ } else {
1397
+ return visible_start;
1398
+ }
1399
+ },
1400
+
1401
+ _calculateVisibleStart(target_item, direction) {
1402
+ if (!target_item) {
1403
+ console.error(
1404
+ "SimpleWidget: _calculateVisibleStart target item is null"
1405
+ );
1406
+ return 0;
1407
+ }
1408
+ this.preAnchorItemIndex = target_item.index;
1409
+ let pos_key = directionFreeKeyMap.pos[this.vertical];
1410
+ let size_key = directionFreeKeyMap.size[this.vertical];
1411
+ let center_key = directionFreeKeyMap.center[this.vertical];
1412
+ let new_visible_start = this.visibleStart;
1413
+
1414
+ switch (this.slideSetting.Type) {
1415
+ case SlideSetting.Type.FIX_POSITION:
1416
+ //FIX_POSITION 模式会将当前 item 的中心固定到指定位置
1417
+ new_visible_start = Math.ceil(
1418
+ target_item[center_key] -
1419
+ this.visibleRange * this.slideSetting.FixPercent
1420
+ );
1421
+ break;
1422
+ case SlideSetting.Type.WHOLE_PAGE:
1423
+ new_visible_start = this.templateParser.GetItem(
1424
+ target_item.pageHeadIndex
1425
+ )[pos_key];
1426
+ break;
1427
+ case SlideSetting.Type.SEAMLESS:
1428
+ if (direction > 0) {
1429
+ if (
1430
+ target_item[pos_key] + target_item[size_key] >
1431
+ this.visibleStart +
1432
+ this.visibleRange * this.slideSetting.EndPercent
1433
+ ) {
1434
+ new_visible_start =
1435
+ target_item[pos_key] +
1436
+ target_item[size_key] -
1437
+ this.visibleRange * this.slideSetting.EndPercent;
1438
+ }
1439
+ } else if (direction < 0) {
1440
+ if (
1441
+ target_item[pos_key] <
1442
+ this.visibleStart +
1443
+ this.visibleRange * this.slideSetting.StartPercent
1444
+ ) {
1445
+ new_visible_start =
1446
+ target_item[pos_key] -
1447
+ this.visibleRange * this.slideSetting.StartPercent;
1448
+ }
1449
+ } else {
1450
+ //不是沿widget方向的移动
1451
+ if (target_item[pos_key] < this.visibleStart) {
1452
+ new_visible_start =
1453
+ target_item[pos_key] -
1454
+ this.visibleRange * this.slideSetting.StartPercent;
1455
+ } else if (
1456
+ target_item[pos_key] + target_item[size_key] >
1457
+ this.visibleStart + this.visibleRange
1458
+ ) {
1459
+ new_visible_start =
1460
+ target_item[pos_key] +
1461
+ target_item[size_key] -
1462
+ this.visibleRange * this.slideSetting.EndPercent;
1463
+ }
1464
+ }
1465
+ break;
1466
+ default:
1467
+ console.error(
1468
+ "SimpleWidget: undefined slide type",
1469
+ this.slideSetting.Type
1470
+ );
1471
+ }
1472
+
1473
+ if (
1474
+ (this.slideSetting.BoundaryProtect & SlideSetting.START_PROTECT) >
1475
+ 0
1476
+ ) {
1477
+ let boundary = 0;
1478
+ if (!this.templateParser.GetItem(0).focusable) {
1479
+ //首个元素是占位符
1480
+ boundary = this.templateParser.GetItem(0)[size_key];
1481
+ }
1482
+ new_visible_start =
1483
+ new_visible_start < boundary ? 0 : new_visible_start;
1484
+ }
1485
+ if ((this.slideSetting.BoundaryProtect & SlideSetting.END_PROTECT) > 0) {
1486
+ let last_item = this.templateParser.GetItem(-1);
1487
+ let last_visible_start =
1488
+ last_item[pos_key] + last_item[size_key] - this.visibleRange;
1489
+ last_visible_start = last_visible_start < 0 ? 0 : last_visible_start;
1490
+ let boundary = last_visible_start;
1491
+ if (!last_item.focusable) {
1492
+ //最后个元素是占位符
1493
+ boundary = last_item[pos_key] - this.visibleRange;
1494
+ }
1495
+ new_visible_start =
1496
+ new_visible_start > boundary ? last_visible_start : new_visible_start;
1497
+ }
1498
+ return new_visible_start;
1499
+ },
1500
+
1501
+ _onFocusableItemEdge(edge_info) {
1502
+ let horizontal_direction = 0;
1503
+ let vertical_direction = 0;
1504
+ switch (edge_info.direction) {
1505
+ case EdgeDirection.left:
1506
+ horizontal_direction = -1;
1507
+ break;
1508
+ case EdgeDirection.right:
1509
+ horizontal_direction = 1;
1510
+ break;
1511
+ case EdgeDirection.top:
1512
+ vertical_direction = -1;
1513
+ break;
1514
+ case EdgeDirection.bottom:
1515
+ vertical_direction = 1;
1516
+ break;
1517
+ default:
1518
+ break;
1519
+ }
1520
+ this._moveToNext(horizontal_direction, vertical_direction, edge_info);
1521
+ },
1522
+
1523
+ _ifValidEnterRect(rect_info) {
1524
+ return rect_info && rect_info.direction && rect_info.rect;
1525
+ },
1526
+
1527
+ _setZIndex(index, z_index) {
1528
+ this.innerData[index].controller.zIndex = z_index;
1529
+ },
1530
+
1531
+ _onFocus() {
1532
+ let focus_id =
1533
+ typeof this.enterFocusId !== "undefined" &&
1534
+ this.enterFocusId >= 0 &&
1535
+ this.enterFocusId < this.templateParser.GetTemplate().List.length
1536
+ ? this.enterFocusId
1537
+ : this.focusId;
1538
+ focus_id = this._ifValidEnterRect(this.enterFocusRect)
1539
+ ? this._calculateNearestItemByRect(
1540
+ this.pageUpdater.getRange(),
1541
+ this.enterFocusRect
1542
+ )
1543
+ : focus_id;
1544
+ this.preEdgeRect = this.enterFocusRect;
1545
+ this.isFocus = true;
1546
+ this.focusId = focus_id;
1547
+ this.preFocusId = -1;
1548
+ this.enterFocusId = -1;
1549
+ this.enterFocusRect = null;
1550
+ this._updateFocusItem();
1551
+ if (
1552
+ this.isFocus &&
1553
+ this.innerData[this.templateParser.FocusIdToIndex(this.focusId)].mounted
1554
+ ) {
1555
+ this.innerData[
1556
+ this.templateParser.FocusIdToIndex(this.focusId)
1557
+ ].itemHandler.onFocus?.(this.preEdgeRect);
1558
+ }
1559
+
1560
+ if (this.onFocus) {
1561
+ this.onFocus();
1562
+ }
1563
+ },
1564
+
1565
+ _onBlur() {
1566
+ this.isFocus = false;
1567
+ this.enterFocusId = -1;
1568
+ this.enterFocusRect = null;
1569
+
1570
+ this.preFocusId = this.focusId;
1571
+ this.preEdgeRect = null;
1572
+ this._updateBlurItem();
1573
+ this.innerData[
1574
+ this.templateParser.FocusIdToIndex(this.preFocusId)
1575
+ ].itemHandler.onBlur?.();
1576
+
1577
+ if (this.onBlur) {
1578
+ this.onBlur();
1579
+ }
1580
+ },
1581
+
1582
+ _onSlideEnd(event) {
1583
+ if (event && event.stopPropagation) {
1584
+ event.stopPropagation();
1585
+ }
1586
+ this.pageUpdater.apply();
1587
+ },
1588
+
1589
+ _onContentUpdate() {
1590
+ this._updateFocusItem();
1591
+ },
1592
+
1593
+ _updateFocusItem() {
1594
+ this._setZIndex(
1595
+ this.templateParser.FocusIdToIndex(this.focusId),
1596
+ this.innerData.length
1597
+ );
1598
+ },
1599
+
1600
+ _updateBlurItem() {
1601
+ this._setZIndex(this.templateParser.FocusIdToIndex(this.preFocusId), 0);
1602
+ },
1603
+
1604
+ _updateFocusByDragInfo(viewX, viewY) {
1605
+ let enterFocusRect = null;
1606
+ //模拟最小区域作为输入区域
1607
+ if (this.direction === VERTICAL) {
1608
+ enterFocusRect = {
1609
+ direction: EdgeDirection.top,
1610
+ rect: {
1611
+ x: this.left - viewX,
1612
+ y: this.top - viewY,
1613
+ width: 10,
1614
+ height: 10,
1615
+ },
1616
+ };
1617
+ } else {
1618
+ enterFocusRect = {
1619
+ direction: EdgeDirection.left,
1620
+ rect: {
1621
+ x: this.left - viewX,
1622
+ y: this.top - viewY,
1623
+ width: 10,
1624
+ height: 10,
1625
+ },
1626
+ };
1627
+ }
1628
+ this.focusId = this._calculateNearestItemByRect(
1629
+ this.pageUpdater.getRange(),
1630
+ enterFocusRect
1631
+ );
1632
+ },
1633
+
1634
+ _registerRootView(ele) {
1635
+ this.rootElement = ele;
1636
+ },
1637
+
1638
+ _tryReoprtVisibleEvent(msg) {
1639
+ if (msg) {
1640
+ return;
1641
+ }
1642
+ // if (typeof msg.viewY != "undefined" && typeof msg.viewX != "undefined") {
1643
+ // if (this.direction === VERTICAL) {
1644
+ // this.visibleStart = -msg.viewY;
1645
+ // } else {
1646
+ // this.visibleStart = -msg.viewX;
1647
+ // }
1648
+ // if (this.visibleStart < 0) {
1649
+ // this.visibleStart = 0;
1650
+ // }
1651
+ // }
1652
+ // let cur_visible_start =
1653
+ // this.visibleStart - this.visibleRangeWithPadding * 2;
1654
+ // let cur_visible_end =
1655
+ // this.visibleStart + this.visibleRangeWithPadding * 3 - 1;
1656
+ // let baseItemId = this.visibleRangeSearchBaseItem
1657
+ // ? this.visibleRangeSearchBaseItem
1658
+ // : 0;
1659
+ // this._updateVisibleList(baseItemId, cur_visible_start, cur_visible_end);
1660
+ },
1661
+
1662
+ _getTouchListener() {
1663
+ if (!this.enableTouch) {
1664
+ return null;
1665
+ }
1666
+ let callback = {
1667
+ OnDragStart: (msg) => {
1668
+ // console.log("SimpleWidget Container OnDragStart:", msg);
1669
+ //删除焦点
1670
+ Promise.resolve().then(() => {
1671
+ this._tryReoprtVisibleEvent(msg);
1672
+ Forge.sRenderBridge.InstantPerformSwap();
1673
+ });
1674
+ return true;
1675
+ },
1676
+ OnMoved: (msg) => {
1677
+ Promise.resolve().then(() => {
1678
+ this._tryReoprtVisibleEvent(msg);
1679
+ Forge.sRenderBridge.InstantPerformSwap();
1680
+ });
1681
+ return true;
1682
+ },
1683
+ OnDragEnd: (msg) => {
1684
+ // console.log("SimpleWidget Container OnDragEnd:", msg);
1685
+ this._tryReoprtVisibleEvent(msg);
1686
+ this._updateFocusByDragInfo(msg["viewX"], msg["viewY"]);
1687
+ this._onSlideEnd();
1688
+ return true;
1689
+ },
1690
+ OnFling: (msg) => {
1691
+ // console.log("SimpleWidget Container OnFling:", msg);
1692
+ this._tryReoprtVisibleEvent(msg);
1693
+ this._updateFocusByDragInfo(msg["viewX"], msg["viewY"]);
1694
+ this._onSlideEnd();
1695
+ return true;
1696
+ },
1697
+ OnRelease: () => {
1698
+ // console.log("SimpleWidget Container OnRelease:", msg);
1699
+ return true;
1700
+ },
1701
+ };
1702
+ return callback;
1703
+ },
1704
+ _getFocusBlockActions() {
1705
+ return {
1706
+ onFocus: this._onFocus,
1707
+ onBlur: this._onBlur,
1708
+ onKeyDown: this.onKeyDown,
1709
+ onCustomerEvent: this._onCustomerEvent,
1710
+ };
1711
+ },
1712
+ _updatePage(range_list) {
1713
+ let list = [];
1714
+ for (let range of range_list) {
1715
+ list = list.concat(this.innerData.slice(range.start, range.end + 1));
1716
+ }
1717
+ this.$refs.rootView?.updateData(list);
1718
+ this.refreshToken++;
1719
+ this.itemRender = false;
1720
+ setTimeout(() => {
1721
+ this.itemRender = true;
1722
+ }, 0);
1723
+ },
1724
+ },
1725
+ created() {
1726
+ this.pageUpdater = new PageUpdater(this._updatePage);
1727
+ if (this.dispatcher) {
1728
+ this.dispatcher.registerComponent(this);
1729
+ }
1730
+ this.innerPadding = _getPadding(this.padding);
1731
+ this.dragDirection =
1732
+ this.direction === VERTICAL
1733
+ ? Forge.DragSetting.DIRECTION_VERTICAL
1734
+ : Forge.DragSetting.DIRECTION_HORIZONTAL;
1735
+ this.visibleRange =
1736
+ this.direction === VERTICAL
1737
+ ? this.height - this.innerPadding.top - this.innerPadding.bottom
1738
+ : this.width - this.innerPadding.left - this.innerPadding.right;
1739
+ this.visibleRangeWithPadding =
1740
+ this.direction === VERTICAL ? this.height : this.width;
1741
+
1742
+ if (this.provideData) {
1743
+ this.dataList = this.provideData();
1744
+ } else if (this.data) {
1745
+ this.dataList = this.data;
1746
+ }
1747
+ let template_parser = _getTemplateParser(
1748
+ this.width,
1749
+ this.height,
1750
+ this.direction,
1751
+ this.padding,
1752
+ this.supportHistoryPath,
1753
+ this.layoutType
1754
+ );
1755
+ this.templateItemAdder = new TemplateItemAdder(
1756
+ template_parser,
1757
+ this.dataList,
1758
+ this.measures,
1759
+ this.visibleRangeWithPadding,
1760
+ this._onTemplateAdd
1761
+ );
1762
+ this.templateItemAdder.tryAddItem(null, 2);
1763
+ this.templateParser = template_parser;
1764
+ let template_list = this.templateParser.GetTemplate().List;
1765
+ let last_item = template_list[template_list.length - 1];
1766
+ this.touchListener = this._getTouchListener();
1767
+ this.touchContainerW =
1768
+ this.direction === VERTICAL
1769
+ ? this.width
1770
+ : last_item.xPos + last_item.width;
1771
+ this.touchContainerH =
1772
+ this.direction === VERTICAL
1773
+ ? last_item.yPos + last_item.height
1774
+ : this.height;
1775
+ this.slidePile = new Forge.RectArea(
1776
+ 0,
1777
+ 0,
1778
+ this.width - this.innerPadding.left - this.innerPadding.right,
1779
+ this.height - this.innerPadding.top - this.innerPadding.bottom
1780
+ );
1781
+ let init_focus_id = 0;
1782
+ let cur_visible_start = 0;
1783
+
1784
+ if (this.initFocusId) {
1785
+ const item = this.templateParser.GetItemById(this.initFocusId);
1786
+ if (item) {
1787
+ init_focus_id = this.initFocusId;
1788
+ cur_visible_start = this._calculateVisibleStart(item, 1);
1789
+ }
1790
+ }
1791
+ this.focusId = init_focus_id;
1792
+ this.visibleStart = cur_visible_start;
1793
+ },
1794
+ mounted() {
1795
+ if (this.visibleStart) {
1796
+ this._slideTo(this.visibleStart, null);
1797
+ }
1798
+ this.pageUpdater.update(
1799
+ this.templateParser,
1800
+ this.visibleStart,
1801
+ this.visibleStart + this.visibleRangeWithPadding - 1,
1802
+ 0
1803
+ );
1804
+ this.pageUpdater.apply();
1805
+ },
1806
+ beforeUnmount() {
1807
+ if (this.dispatcher) {
1808
+ this.dispatcher.unregisterComponent();
1809
+ }
1810
+ },
1811
+ };
1812
+
1813
+ export default SimpleWidget;
1814
+ </script>
1815
+
1816
+ <template>
1817
+ <jsv-focus-block
1818
+ ref="focusNode"
1819
+ :name="name"
1820
+ :onAction="_getFocusBlockActions()"
1821
+ >
1822
+ <div
1823
+ ref="rootDiv"
1824
+ :style="{
1825
+ left: left,
1826
+ top: top,
1827
+ width: width,
1828
+ height: height,
1829
+ overflow: 'hidden',
1830
+ }"
1831
+ >
1832
+ <root-view
1833
+ ref="rootView"
1834
+ :refreshToken="refreshToken"
1835
+ :dataUpdateToken="dataUpdateToken"
1836
+ :x="innerPadding.left"
1837
+ :y="innerPadding.top"
1838
+ :width="touchContainerW"
1839
+ :height="touchContainerH"
1840
+ :flingPageWidth="flingPageWidth"
1841
+ :flingPageEdge="flingPageEdge"
1842
+ :slidePile="slidePile"
1843
+ :direction="dragDirection"
1844
+ :touchListener="touchListener"
1845
+ :enableTouch="enableTouch"
1846
+ :loadAll="loadAll"
1847
+ :register="_registerRootView"
1848
+ :onLoad="_onContentUpdate"
1849
+ :onItemEdge="_onFocusableItemEdge"
1850
+ :widgetHandler="_injectHandler()"
1851
+ :enableItemRenderBreak="enableItemRenderBreak"
1852
+ :placeHolderSetting="placeHolderSetting"
1853
+ >
1854
+ <template v-slot:renderItem="slotProps">
1855
+ <slot name="renderItem" v-bind="slotProps"></slot>
1856
+ </template>
1857
+ </root-view>
1858
+ </div>
1859
+ </jsv-focus-block>
1860
+ </template>