@shijiu/jsview-vue-samples 2.1.482-test.0 → 2.2.128

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 (39) hide show
  1. package/BakeViewDemo/AnimatePic.vue +93 -0
  2. package/BakeViewDemo/App.vue +109 -0
  3. package/ClickDivDemo/App.vue +150 -0
  4. package/DemoHomepage/components/Dialog.vue +37 -11
  5. package/DemoHomepage/components/TabFrame.vue +8 -1
  6. package/DemoHomepage/router.js +76 -34
  7. package/DemoHomepage/views/Homepage.vue +30 -13
  8. package/FocusMoveStyle/App.vue +21 -4
  9. package/FocusMoveStyle/FoldableItem.vue +39 -12
  10. package/FreeMoveChildAttract/App.vue +105 -0
  11. package/FreeMoveLink/App.vue +55 -0
  12. package/FreeMoveResize/App.vue +102 -0
  13. package/LongText/App.vue +1 -3
  14. package/LongText/LongTextScroll.vue +124 -67
  15. package/LongTextLatex/App.vue +93 -0
  16. package/LongTextLatex/Button.vue +50 -0
  17. package/LongTextLatex/ButtonItem.vue +44 -0
  18. package/LongTextLatex/LongTextScroll.vue +189 -0
  19. package/LongTextLatex/Scroll.vue +14 -0
  20. package/Marquee/longText.js +1 -1
  21. package/MetroWidgetDemos/MassiveItems/App.vue +87 -0
  22. package/MetroWidgetDemos/MassiveItems/ContentItem.vue +388 -0
  23. package/MetroWidgetDemos/MassiveItems/Item.vue +203 -0
  24. package/MetroWidgetDemos/MassiveItems/WidgetItem.vue +101 -0
  25. package/MetroWidgetDemos/MassiveItems/data.js +17 -0
  26. package/MetroWidgetDemos/PingPong/AppPage.vue +19 -9
  27. package/MetroWidgetDemos/SeamlessSlide/App.vue +156 -0
  28. package/MetroWidgetDemos/SeamlessSlide/ControlItem.vue +45 -0
  29. package/MetroWidgetDemos/SeamlessSlide/Controller.vue +110 -0
  30. package/MetroWidgetDemos/SeamlessSlide/Item.vue +43 -0
  31. package/MetroWidgetDemos/routeList.js +12 -0
  32. package/SceneTransition/App.vue +10 -2
  33. package/ScrollBoxTest/App.vue +160 -0
  34. package/TestNativeSharedView/App.vue +166 -73
  35. package/TextureAnimation/App2.vue +52 -7
  36. package/TombSweepingDayTest/App.vue +11 -0
  37. package/TombSweepingDayTest/Raining/Rain.vue +69 -0
  38. package/TombSweepingDayTest/Raining/RainScene.vue +131 -0
  39. package/package.json +1 -1
@@ -0,0 +1,93 @@
1
+ <script setup>
2
+ import { onMounted, onUnmounted, shallowRef } from "vue";
3
+ import { getDataUrl } from "../CommonUtils/ResourceData";
4
+ //无网络环境下使用
5
+ const DemoResourceBase = getDataUrl();
6
+ const item_url = DemoResourceBase
7
+ ? `${DemoResourceBase}/27bda620942566673ab449a3ef765321.png`
8
+ : "http://oss.image.qcast.cn/homepage/20210209/27bda620942566673ab449a3ef765321.png";
9
+
10
+ const staticLeftPos = shallowRef(0);
11
+ let moveLeftTimer = -1;
12
+
13
+ onMounted(() => {
14
+ moveLeftTimer = setInterval(() => {
15
+ if (staticLeftPos.value > 200) {
16
+ staticLeftPos.value = 0;
17
+ } else {
18
+ staticLeftPos.value = staticLeftPos.value + 10;
19
+ }
20
+ }, 800);
21
+ });
22
+
23
+ onUnmounted(() => {
24
+ if (moveLeftTimer != -1) {
25
+ clearInterval(moveLeftTimer);
26
+ moveLeftTimer = -1;
27
+ }
28
+ });
29
+ </script>
30
+
31
+ <template>
32
+ <div>
33
+ <div
34
+ :style="{
35
+ left: staticLeftPos,
36
+ width: 50,
37
+ height: 50,
38
+ backgroundImage: `url(${item_url})`,
39
+ }"
40
+ />
41
+ <div
42
+ className="blockStyle"
43
+ :style="{
44
+ backgroundColor: '#0000FF',
45
+ animation: 'opacityDemo_CompositeNoOpacity 3s infinite',
46
+ }"
47
+ />
48
+ <img
49
+ className="blockStyle"
50
+ :style="{
51
+ objectFit: 'contain',
52
+ animation: 'opacityDemo_CompositeOpacity 3s infinite',
53
+ }"
54
+ :src="item_url"
55
+ />
56
+ </div>
57
+ </template>
58
+
59
+ <style scoped>
60
+ .blockStyle {
61
+ top: 50;
62
+ left: 50;
63
+ width: 150;
64
+ height: 150;
65
+ }
66
+
67
+ .picTitleTextClass {
68
+ font-size: 25;
69
+ height: 68;
70
+ line-height: 34;
71
+ color: #000000;
72
+ text-align: "center";
73
+ }
74
+
75
+ @keyframes opacityDemo_CompositeOpacity {
76
+ from {
77
+ transform: translate3d(50%, 30, 0) scale3d(1.5, 1.5, 1)
78
+ rotate3d(1.5, 1, 0, 90deg) skew(30deg, 45deg);
79
+ opacity: 0.1;
80
+ }
81
+ to {
82
+ }
83
+ }
84
+
85
+ @keyframes opacityDemo_CompositeNoOpacity {
86
+ from {
87
+ transform: translate3d(50%, 30, 0) scale3d(1.5, 1.5, 1)
88
+ rotate3d(1.5, 1, 0, 90deg) skew(30deg, 45deg);
89
+ }
90
+ to {
91
+ }
92
+ }
93
+ </style>
@@ -0,0 +1,109 @@
1
+ <!--
2
+ * @Author: ChenChanghua
3
+ * @Date: 2022-04-20 21:05:26
4
+ * @LastEditors: ChenChanghua
5
+ * @LastEditTime: 2022-07-08 11:16:52
6
+ * @Description: file content
7
+ -->
8
+ <script setup>
9
+ import { onMounted, onUnmounted, shallowRef } from "vue";
10
+ import { useRouter } from "vue-router";
11
+ import AnimatePic from "./AnimatePic.vue";
12
+ import { jJsvRuntimeBridge, JsvTextureStoreApi, JsvFilterView } from "jsview";
13
+
14
+ const router = useRouter();
15
+
16
+ const vCapturedTexture = shallowRef(null);
17
+ // const vCapturedTexture = shallowRef("123");
18
+ const vToCapture = shallowRef(null);
19
+
20
+ window.MyCapture = vToCapture;
21
+
22
+ const _onKeyDown = (ev) => {
23
+ if (ev.keyCode == 8 || ev.keyCode == 27 || ev.keyCode == 10000) {
24
+ router?.go(-1); // 有router时,是从DemoHomepage进入,回退
25
+ return true;
26
+ }
27
+ };
28
+
29
+ let loopCaptureTimer = -1;
30
+
31
+ onMounted(() => {
32
+ jJsvRuntimeBridge.notifyPageLoaded();
33
+
34
+ loopCaptureTimer = setInterval(() => {
35
+ JsvTextureStoreApi.capture2Texture(
36
+ vToCapture.value,
37
+ (textureName, autoRecycle, w, h) => {
38
+ vCapturedTexture.value = textureName;
39
+ console.log(`capture done id=${textureName} w=${w} h=${h}`);
40
+ }
41
+ );
42
+ }, 2200);
43
+ });
44
+
45
+ onUnmounted(() => {
46
+ if (loopCaptureTimer != -1) {
47
+ clearInterval(loopCaptureTimer);
48
+ loopCaptureTimer = -1;
49
+ }
50
+ });
51
+ </script>
52
+
53
+ <template>
54
+ <jsv-focus-block
55
+ autoFocus
56
+ :onAction="{
57
+ onKeyDown: _onKeyDown,
58
+ }"
59
+ >
60
+ <div
61
+ :style="{
62
+ textAlign: 'center',
63
+ fontSize: 30,
64
+ lineHeight: 50,
65
+ color: '#ffffff',
66
+ left: 140,
67
+ top: 20,
68
+ width: 1000,
69
+ height: 50,
70
+ backgroundColor: 'rgba(27,38,151,0.8)',
71
+ }"
72
+ >
73
+ 左边的div会被制成快照(每2秒)显示在右图
74
+ </div>
75
+ <div
76
+ ref="vToCapture"
77
+ :style="{
78
+ left: 140,
79
+ top: 200,
80
+ width: 300, // 截屏区域的宽
81
+ height: 300, // 截屏区域的高
82
+ }"
83
+ >
84
+ <jsv-filter-view :width="300" :height="300">
85
+ <div
86
+ :style="{
87
+ width: 300,
88
+ height: 300,
89
+ backgroundColor: 'rgb(128, 128, 0)',
90
+ }"
91
+ />
92
+ <animate-pic />
93
+ </jsv-filter-view>
94
+ </div>
95
+
96
+ <div
97
+ v-if="vCapturedTexture != null"
98
+ :style="{
99
+ top: 200,
100
+ left: 650,
101
+ width: 150,
102
+ height: 150,
103
+ backgroundImage: `jsvtexturestore://${vCapturedTexture}`,
104
+ }"
105
+ />
106
+ </jsv-focus-block>
107
+ </template>
108
+
109
+ <style scoped></style>
@@ -0,0 +1,150 @@
1
+ <!--
2
+ * @Author: ChenChanghua
3
+ * @Date: 2022-08-30 10:58:38
4
+ * @LastEditors: ChenChanghua
5
+ * @LastEditTime: 2022-08-30 14:20:46
6
+ * @Description: file content
7
+ -->
8
+ <script setup>
9
+ import { shallowRef, onMounted } from "vue";
10
+ import { useRouter } from "vue-router";
11
+
12
+ const router = useRouter();
13
+
14
+ const onKeyDown = (ev) => {
15
+ if (ev.keyCode == 8 || ev.keyCode == 27 || ev.keyCode == 10000) {
16
+ router?.go(-1); // 有router时,是从DemoHomepage进入,回退
17
+ }
18
+ return true;
19
+ };
20
+
21
+ // @click处理项目的响应处理
22
+ const clickItemColor = shallowRef("#c3aa20");
23
+ const onClickItemClick = () => {
24
+ clickItemColor.value = "#ba69af";
25
+ // 点击后改变背景颜色来提示
26
+ setTimeout(() => {
27
+ clickItemColor.value = "#c3aa20";
28
+ }, 150);
29
+ };
30
+
31
+ // TapListener处理项目的响应处理
32
+ const tapItemColor = shallowRef("#c3aa20");
33
+ const tapDivRef = shallowRef(null);
34
+ const registerTap = () => {
35
+ tapDivRef.value.jsvSetTapListener({
36
+ // 长按响应,以变色来回应
37
+ onLongPress: () => {
38
+ tapItemColor.value = "#25ac47";
39
+ console.log("Long pressed");
40
+ },
41
+
42
+ // 长按响应结束,以颜色还原来回应
43
+ onLongPressRelease: () => {
44
+ tapItemColor.value = "#c3aa20";
45
+ console.log("Long pressed release");
46
+ },
47
+
48
+ // 长按响应取消,通过长按 + 不放手时移动阿里激活,以颜色还原来回应
49
+ onCancel: () => {
50
+ tapItemColor.value = "#c3aa20";
51
+ console.log("canceled");
52
+ },
53
+
54
+ // 同样的click处理,以blink来回应
55
+ onClick: () => {
56
+ console.log("onClicked");
57
+ tapItemColor.value = "#ba69af";
58
+ // 点击后改变背景颜色来提示
59
+ setTimeout(() => {
60
+ tapItemColor.value = "#c3aa20";
61
+ }, 150);
62
+ },
63
+ });
64
+ };
65
+
66
+ onMounted(() => {
67
+ registerTap();
68
+ });
69
+ </script>
70
+
71
+ <template>
72
+ <jsv-focus-block
73
+ autoFocus
74
+ :style="{
75
+ width: 1920,
76
+ height: 1080,
77
+ backgroundColor: '#007788',
78
+ }"
79
+ :onAction="{
80
+ onKeyDown: onKeyDown,
81
+ }"
82
+ >
83
+ <div
84
+ class="baseTextSet"
85
+ :style="{
86
+ lineHeight: 50,
87
+ left: 140,
88
+ top: 20,
89
+ width: 1000,
90
+ height: 50,
91
+ backgroundColor: 'rgba(27,38,151,0.8)',
92
+ }"
93
+ >
94
+ 展示两种接受点击事件div的写法
95
+ </div>
96
+
97
+ <div
98
+ class="baseTextSet clickItem"
99
+ :style="{
100
+ left: 300,
101
+ top: 230,
102
+ backgroundColor: clickItemColor,
103
+ }"
104
+ @click="onClickItemClick"
105
+ >
106
+ {{ "@click" }}
107
+ </div>
108
+ <div
109
+ class="baseTextSet"
110
+ :style="{ left: 174, top: 350, width: 400, height: 50, lineHeight: 50 }"
111
+ >
112
+ {{ "点击会闪紫色" }}
113
+ </div>
114
+
115
+ <div
116
+ class="baseTextSet clickItem"
117
+ ref="tapDivRef"
118
+ :style="{
119
+ left: 600,
120
+ top: 230,
121
+ backgroundColor: tapItemColor,
122
+ width: 320,
123
+ lineHeight: 50,
124
+ }"
125
+ >
126
+ {{ "TapListener\n支持Click/LongPress" }}
127
+ </div>
128
+
129
+ <div
130
+ class="baseTextSet"
131
+ :style="{ left: 550, top: 350, width: 400, height: 100, lineHeight: 50 }"
132
+ >
133
+ {{ "点击会闪紫色\n长按会出现绿色" }}
134
+ </div>
135
+ </jsv-focus-block>
136
+ </template>
137
+
138
+ <style scoped>
139
+ .baseTextSet {
140
+ text-align: center;
141
+ font-size: 30;
142
+ color: #ffffff;
143
+ }
144
+
145
+ .clickItem {
146
+ width: 150;
147
+ height: 100;
148
+ line-height: 100;
149
+ }
150
+ </style>
@@ -1,11 +1,20 @@
1
1
  <script setup>
2
- import { shallowRef } from "vue"
2
+ import { shallowRef, watchEffect } from "vue";
3
+ import { useFocusHub } from "jsview";
3
4
  const props = defineProps({
4
5
  name: String,
5
6
  onAction: Function,
6
- })
7
+ focused: Boolean,
8
+ });
7
9
 
8
- let focusId = shallowRef(0)
10
+ let focusId = shallowRef(0);
11
+
12
+ const focusHub = useFocusHub();
13
+ watchEffect(() => {
14
+ if (props.focused) {
15
+ focusHub.setFocus(props.name);
16
+ }
17
+ });
9
18
 
10
19
  const onKeyDown = (ev) => {
11
20
  switch (ev.keyCode) {
@@ -29,25 +38,42 @@ const onKeyDown = (ev) => {
29
38
  default:
30
39
  }
31
40
  return true;
32
- }
41
+ };
33
42
  const actionDefines = {
34
- onKeyDown
35
- }
43
+ onKeyDown,
44
+ };
45
+
46
+ const funcDialogOuterClick = () => {
47
+ console.log("Clicked on dialog outer");
48
+ props.onAction("back");
49
+ };
50
+
51
+ const funcDialogBackClick = () => {
52
+ console.log("Clicked on dialog back");
53
+ };
54
+
55
+ const funcDialogOKClick = () => {
56
+ props.onAction("ok");
57
+ };
36
58
 
59
+ const funcDialogNGClick = () => {
60
+ props.onAction("cancel");
61
+ };
37
62
  </script>
38
63
 
39
64
  <template>
40
- <jsv-focus-block autoFocus :name="props.name" :onAction="actionDefines">
65
+ <jsv-focus-block :name="props.name" :onAction="actionDefines">
66
+ <div :style="{ width: 1280, height: 720 }" @click="funcDialogOuterClick" />
41
67
  <div :style="{ top: 200, left: 465 }">
42
- <div class="background"></div>
68
+ <div class="background" @click="funcDialogBackClick"></div>
43
69
  <div class="message">是否退出</div>
44
70
  <div :style="{ top: 120, left: 30 }">
45
71
  <div v-if="focusId == 0" class="focus"></div>
46
- <div class="normal">确定</div>
72
+ <div class="normal" @click="funcDialogOKClick">确定</div>
47
73
  </div>
48
74
  <div :style="{ top: 120, left: 220 }">
49
75
  <div v-if="focusId == 1" class="focus"></div>
50
- <div class="normal">取消</div>
76
+ <div class="normal" @click="funcDialogNGClick">取消</div>
51
77
  </div>
52
78
  </div>
53
79
  </jsv-focus-block>
@@ -86,4 +112,4 @@ const actionDefines = {
86
112
  font-size: 40;
87
113
  text-align: center;
88
114
  }
89
- </style>
115
+ </style>
@@ -46,10 +46,17 @@ const data = [
46
46
  {
47
47
  width: 200,
48
48
  height: 50,
49
- name: "游戏实例",
49
+ name: "触控",
50
50
  focusable: true,
51
51
  id: 3,
52
52
  },
53
+ {
54
+ width: 200,
55
+ height: 50,
56
+ name: "游戏实例",
57
+ focusable: true,
58
+ id: 4,
59
+ },
53
60
  ];
54
61
  const direction = VERTICAL;
55
62
  </script>
@@ -16,6 +16,16 @@ let routeList = [
16
16
  path: '/feature/Basic',
17
17
  component: () => import('jsview-vue-samples/Basic/App.vue'),
18
18
  },
19
+ // {
20
+ // name: 'FreeMoveResize',
21
+ // path: '/feature/FreeMoveResize',
22
+ // component: () => import('jsview-vue-samples/FreeMoveResize/App.vue'),
23
+ // },
24
+ {
25
+ name: 'div快照功能',
26
+ path: '/feature/BakeViewDemo',
27
+ component: () => import('jsview-vue-samples/BakeViewDemo/App.vue'),
28
+ },
19
29
  {
20
30
  name: 'FlexDemo',
21
31
  path: '/feature/FlexDemo',
@@ -68,11 +78,6 @@ let routeList = [
68
78
  path: '/feature/GridDemo',
69
79
  component: () => import('jsview-vue-samples/GridDemo/App.vue'),
70
80
  },
71
- {
72
- name: '.9图焦点框漂移',
73
- path: '/Operations/NinePatchDemo',
74
- component: () => import('jsview-vue-samples/NinePatchDemo/App.vue'),
75
- },
76
81
  {
77
82
  name: '有命名空间的焦点切换',
78
83
  path: '/feature/BasicFocusControl',
@@ -99,25 +104,15 @@ let routeList = [
99
104
  path: '/feature/TextureSize',
100
105
  component: () => import('jsview-vue-samples/TextureSize/App.vue'),
101
106
  },
102
- {
103
- name: '动图',
104
- path: '/Operations/AnimPicture',
105
- component: () => import('jsview-vue-samples/AnimPicture/App.vue'),
106
- },
107
- {
108
- name: '粒子效果',
109
- path: '/Operations/SprayView',
110
- component: () => import('jsview-vue-samples/SprayView/App.vue'),
111
- },
112
107
  {
113
108
  name: '长文字',
114
109
  path: '/feature/LongText',
115
110
  component: () => import('jsview-vue-samples/LongText/App.vue'),
116
111
  },
117
112
  {
118
- name: '长图片',
119
- path: '/Operations/LongImage',
120
- component: () => import('jsview-vue-samples/LongImage/App.vue'),
113
+ name: '长文字Latex',
114
+ path: '/feature/LongTextLatex',
115
+ component: () => import('jsview-vue-samples/LongTextLatex/App.vue'),
121
116
  },
122
117
  {
123
118
  name: '二维码',
@@ -134,11 +129,12 @@ let routeList = [
134
129
  path: '/feature/TextBox',
135
130
  component: () => import('jsview-vue-samples/TextBox/App.vue'),
136
131
  },
137
- {
138
- name: '抛物运动写法样例',
139
- path: '/Game/ThrowMoveDemo',
140
- component: () => import('jsview-vue-samples/ThrowMoveDemo/App.vue'),
141
- },
132
+ // TODO: 改为FreeMove后再激活
133
+ // {
134
+ // name: '抛物运动写法样例',
135
+ // path: '/Game/ThrowMoveDemo',
136
+ // component: () => import('jsview-vue-samples/ThrowMoveDemo/App.vue'),
137
+ // },
142
138
  {
143
139
  name: '精灵图',
144
140
  path: '/Game/SpriteImage',
@@ -154,6 +150,31 @@ let routeList = [
154
150
  path: '/feature/Input',
155
151
  component: () => import('jsview-vue-samples/Input/App.vue'),
156
152
  },
153
+ {
154
+ name: '情景主题效果:雨',
155
+ path: '/Operations/TombSweepingDayTest',
156
+ component: () => import('jsview-vue-samples/TombSweepingDayTest/App.vue'),
157
+ },
158
+ {
159
+ name: '.9图焦点框漂移',
160
+ path: '/Operations/NinePatchDemo',
161
+ component: () => import('jsview-vue-samples/NinePatchDemo/App.vue'),
162
+ },
163
+ {
164
+ name: '动图',
165
+ path: '/Operations/AnimPicture',
166
+ component: () => import('jsview-vue-samples/AnimPicture/App.vue'),
167
+ },
168
+ {
169
+ name: '粒子效果',
170
+ path: '/Operations/SprayView',
171
+ component: () => import('jsview-vue-samples/SprayView/App.vue'),
172
+ },
173
+ {
174
+ name: '长图片',
175
+ path: '/Operations/LongImage',
176
+ component: () => import('jsview-vue-samples/LongImage/App.vue'),
177
+ },
157
178
  {
158
179
  name: '拼图demo',
159
180
  path: '/Operations/MaskClip',
@@ -244,11 +265,12 @@ let routeList = [
244
265
  path: '/Game/RadarChart',
245
266
  component: () => import('jsview-vue-samples/JsvRadarChart/App.vue')
246
267
  },
247
- {
248
- name: '跑酷游戏',
249
- path: '/Game/Parkour',
250
- component: () => import('jsview-vue-samples/Parkour/App.vue')
251
- },
268
+ // TODO: 等改为对接FreeMoveActor后再恢复
269
+ // {
270
+ // name: '跑酷游戏',
271
+ // path: '/Game/Parkour',
272
+ // component: () => import('jsview-vue-samples/Parkour/App.vue')
273
+ // },
252
274
  {
253
275
  name: 'Hash参数',
254
276
  path: '/feature/HashParams',
@@ -298,12 +320,7 @@ let routeList = [
298
320
  name: 'latex公式',
299
321
  path: '/feature/LatexFormula',
300
322
  component: () => import('jsview-vue-samples/LatexFormula/App.vue'),
301
- },
302
- {
303
- name: '触控示例',
304
- path: '/feature/TouchWidget',
305
- component: () => import('jsview-vue-samples/TouchWidget/App.vue'),
306
- },
323
+ },
307
324
  {
308
325
  name: 'Swiper3dTest',
309
326
  path: '/Operations/Swiper3D',
@@ -324,6 +341,31 @@ let routeList = [
324
341
  path: '/Operations/SceneTransition',
325
342
  component: () => import('jsview-vue-samples/SceneTransition/App.vue'),
326
343
  },
344
+ {
345
+ name: '点击Div',
346
+ path: '/touch/ClickDivDemo',
347
+ component: () => import('jsview-vue-samples/ClickDivDemo/App.vue'),
348
+ },
349
+ {
350
+ name: '触控示例',
351
+ path: '/touch/TouchWidget',
352
+ component: () => import('jsview-vue-samples/TouchWidget/App.vue'),
353
+ },
354
+ // {
355
+ // name: 'FreeMove触控磁吸',
356
+ // path: '/touch/FreeMoveChildAttract',
357
+ // component: () => import('jsview-vue-samples/FreeMoveChildAttract/App.vue'),
358
+ // },
359
+ // {
360
+ // name: 'FreeMoveLink',
361
+ // path: '/touch/FreeMoveLink',
362
+ // component: () => import('jsview-vue-samples/FreeMoveLink/App.vue'),
363
+ // },
364
+ {
365
+ name: '可拖拽进度条',
366
+ path: '/touch/ScrollBoxTest',
367
+ component: () => import('jsview-vue-samples/ScrollBoxTest/App.vue'),
368
+ },
327
369
  ];
328
370
 
329
371
  //添加MetroWidget demo