完成 toolbar
This commit is contained in:
parent
a3f6d7a49d
commit
a5ae341b4e
BIN
public/CascadiaCode.woff2
Normal file
BIN
public/CascadiaCode.woff2
Normal file
Binary file not shown.
105
public/animation.css
Normal file
105
public/animation.css
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
:root {
|
||||||
|
--main-during: 0.35s;
|
||||||
|
--fade-during: .5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-animation-effect {
|
||||||
|
transition: var(--animation-5s);
|
||||||
|
-webkit-transition: var(--animation-5s);
|
||||||
|
-ms-transition: var(--animation-5s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-hidden {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-fade-enter-from,
|
||||||
|
.main-fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.main-fade-enter-to,
|
||||||
|
.main-fade-leave-from {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.main-fade-enter-active,
|
||||||
|
.main-fade-leave-active {
|
||||||
|
transition: opacity var(--main-during);
|
||||||
|
-moz-transition: opacity var(--main-during);
|
||||||
|
-webkit-transition: opacity var(--main-during);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-enter-active,
|
||||||
|
.slide-leave-active {
|
||||||
|
transition: all .5s ease-out;
|
||||||
|
-moz-transition: all .5s ease-out;
|
||||||
|
-webkit-transition: all .5s ease-out;
|
||||||
|
}
|
||||||
|
.slide-enter-from {
|
||||||
|
position: relative;
|
||||||
|
transform: translateY(-100px);
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
.slide-leave-to {
|
||||||
|
transform: translateY(100px);
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.slide-down-enter-active,
|
||||||
|
.slide-down-leave-active {
|
||||||
|
transition: all .5s ease-out;
|
||||||
|
-moz-transition: all .5s ease-out;
|
||||||
|
-webkit-transition: all .5s ease-out;
|
||||||
|
}
|
||||||
|
.slide-down-enter-from {
|
||||||
|
position: relative;
|
||||||
|
transform: translateY(100px);
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
.slide-down-leave-to {
|
||||||
|
transform: translateY(100px);
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-active,
|
||||||
|
.slide-up-leave-active {
|
||||||
|
transition: all .5s ease-out;
|
||||||
|
-moz-transition: all .5s ease-out;
|
||||||
|
-webkit-transition: all .5s ease-out;
|
||||||
|
}
|
||||||
|
.slide-up-enter-from {
|
||||||
|
position: relative;
|
||||||
|
transform: translateY(-100px);
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
.slide-up-leave-to {
|
||||||
|
transform: translateY(-100px);
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-from-top-enter-active,
|
||||||
|
.collapse-from-top-leave-active {
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
-moz-transition: var(--animation-3s);
|
||||||
|
-webkit-transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse-from-top-enter-from {
|
||||||
|
transform: scaleY(0);
|
||||||
|
transform-origin: center top;
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
.collapse-from-top-leave-to {
|
||||||
|
transform: scaleY(0);
|
||||||
|
transform-origin: center top;
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading-mask {
|
||||||
|
0% {
|
||||||
|
background-position: 100% 50%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 0 50%;
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,92 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 4440655 */
|
font-family: "iconfont"; /* Project id 4440655 */
|
||||||
src: url('iconfont.woff2?t=1715948037690') format('woff2');
|
src: url('iconfont.woff2?t=1723382481292') format('woff2');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
font-family: "iconfont" !important;
|
font-family: "iconfont" !important;
|
||||||
|
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-add-line:before {
|
||||||
|
content: "\e7b0";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-arrow-to-previous:before {
|
||||||
|
content: "\e616";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-arrow-to-next:before {
|
||||||
|
content: "\e615";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-beginning:before {
|
||||||
|
content: "\e60e";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-ending:before {
|
||||||
|
content: "\e60f";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-line-analog:before {
|
||||||
|
content: "\e662";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-ladder-analog:before {
|
||||||
|
content: "\e87b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-common-digital:before {
|
||||||
|
content: "\e606";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-delete:before {
|
||||||
|
content: "\e684";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-empty:before {
|
||||||
|
content: "\e617";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-expand:before {
|
||||||
|
content: "\e658";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-collapse:before {
|
||||||
|
content: "\e676";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-group:before {
|
||||||
|
content: "\e70c";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-cancel-group:before {
|
||||||
|
content: "\e65f";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-delete-group:before {
|
||||||
|
content: "\e675";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-choose:before {
|
||||||
|
content: "\e63b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-thin-arrow-right:before {
|
||||||
|
content: "\e622";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-change-color:before {
|
||||||
|
content: "\e6fb";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-join-group:before {
|
||||||
|
content: "\e914";
|
||||||
|
}
|
||||||
|
|
||||||
.icon-arrow-right:before {
|
.icon-arrow-right:before {
|
||||||
content: "\eb08";
|
content: "\eb08";
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@ -11,6 +11,7 @@
|
|||||||
<link rel="stylesheet" href="vscode.css">
|
<link rel="stylesheet" href="vscode.css">
|
||||||
<link rel="stylesheet" href="vcd.css">
|
<link rel="stylesheet" href="vcd.css">
|
||||||
<link rel="stylesheet" href="iconfont.css">
|
<link rel="stylesheet" href="iconfont.css">
|
||||||
|
<link rel="stylesheet" href="animation.css">
|
||||||
<!-- <script src="vcd.js"></script> -->
|
<!-- <script src="vcd.js"></script> -->
|
||||||
<script></script>
|
<script></script>
|
||||||
|
|
||||||
|
@ -9,7 +9,9 @@
|
|||||||
--time-scale-height: 50px;
|
--time-scale-height: 50px;
|
||||||
--sidebar-padding: 10px;
|
--sidebar-padding: 10px;
|
||||||
--sidebar-item-margin: 5px;
|
--sidebar-item-margin: 5px;
|
||||||
|
--toolbar-height: 50px;
|
||||||
--vcd-render-padding: 30px;
|
--vcd-render-padding: 30px;
|
||||||
|
--vcd-value-font-family: "Cascadia code", monospace;
|
||||||
/* 需要满足如下公式 --time-scale-height + --sidebar-padding = --vcd-render-padding + canvas预留 高度 */
|
/* 需要满足如下公式 --time-scale-height + --sidebar-padding = --vcd-render-padding + canvas预留 高度 */
|
||||||
--render-scale-x: 1;
|
--render-scale-x: 1;
|
||||||
|
|
||||||
@ -20,6 +22,11 @@
|
|||||||
--gray-box-shadow-0: 0 0 8px 3px rgba(182, 181, 182, 0.9);
|
--gray-box-shadow-0: 0 0 8px 3px rgba(182, 181, 182, 0.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Cascadia code";
|
||||||
|
src: url("./CascadiaCode.woff2");
|
||||||
|
}
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
background-color: var(--background);
|
background-color: var(--background);
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
@ -36,7 +43,7 @@ body::-webkit-scrollbar {
|
|||||||
height: 2px;
|
height: 2px;
|
||||||
width: 95%;
|
width: 95%;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
}
|
}
|
||||||
@ -68,15 +75,17 @@ body::-webkit-scrollbar {
|
|||||||
::-webkit-scrollbar-corner {
|
::-webkit-scrollbar-corner {
|
||||||
background: none;
|
background: none;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
} */
|
||||||
|
|
||||||
.el-select__wrapper {
|
.el-select__wrapper {
|
||||||
background-color: transparent !important;
|
min-width: 100px;
|
||||||
width: 50% !important;
|
padding: 13px;
|
||||||
min-width: 200px !important;
|
font-size: 16px;
|
||||||
padding: 13px !important;
|
color: var(--sidebar-item-text);
|
||||||
font-size: 16px !important;
|
}
|
||||||
color: var(--sidebar-item-text) !important;
|
|
||||||
|
.el-select-group__title {
|
||||||
|
color: var(--main-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-select__placeholder {
|
.el-select__placeholder {
|
||||||
@ -84,19 +93,14 @@ body::-webkit-scrollbar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-select-dropdown {
|
.el-select-dropdown {
|
||||||
min-width: 300px !important;
|
|
||||||
background-color: var(--sidebar);
|
background-color: var(--sidebar);
|
||||||
border: 1.0px solid var(--main-color);
|
border: 1.0px solid var(--main-color);
|
||||||
outline: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-checkbox-button__inner {
|
.el-checkbox-button__inner {
|
||||||
font-size: 16px !important;
|
font-size: 16px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-select {
|
|
||||||
width: fit-content !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--main-color);
|
color: var(--main-color);
|
||||||
|
40
scripts/update_icon.py
Normal file
40
scripts/update_icon.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import requests as r
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
# 下载 压缩包
|
||||||
|
headers = {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
|
||||||
|
'Cookie': 'cna=FeNcHjgPWA8CAXU/P9lD8IsF; EGG_SESS_ICONFONT=Hu68kBY7XO7C6Udp3T99M1asKmUZ0gxjps8xjTrjx4ZtNCIR_nFu9Li15nxoPAWLmGlcEMN2KEQyAvgBfASR3cSsmd2lhqg89lUmApzbWgBgCWjMwMzjawMqh2KNT8kCICxit3iWC7YLdUuCdUfXg_cGkRjPNvDohqyeHF27gTb5CloBSvLjqN45PcUvcUig; ctoken=Ku-GfnHTFQU6ObMjjX4rrwYn; u=10114852; u.sig=mv5vi-TPPlhvQJi2PMIC4VoPpD03Wc9UykMTMiG6ElA; xlly_s=1; tfstk=fSrIwBNqyBACtg2sZ2BZ5BUbpIn7PWsVNLM8n8KeeDnpwQex_JrEY887N5wxykyrvui717NU8DUUPYN4p7glKbl-N7y88OSV0J2nq0K5giSVfSIC9W9pv3B-6YMoT58eCJ2nqdvwwZzTKU6xnQBS27ntXvkj2HHKwO9tnfKKeHHJ6OMo6bn-9v39XxMD9e3Kpbdy1Y7IeJ69Kr6y7I-gLftJcKDKJZyez3-yav0nMVh62Arsd2GYpk3XwyMTblgq7L5ZX-4a9AifbenYCzFbk7b2SDw8J70_t1Ys_PE3eb3wenysPXaTvV9J2RmsN447O6Tn9yPsoA39FiDagfe3vP6k6JFqODHt7iBbB4UaxqqF6HiYoJoUy7b2SDw8JcszunlfpXTWCqxSCjW1CUYrE4jk1CVx8l3KIvIVCOO6r2HiCjW1CUYoJADLuO661Uf..; isg=BCwsfWsQZki1QXEWw0jCCc4h_Qpe5dCP-aVamIZsF1d6kc6br_ZyHmFnsVkpGQjn',
|
||||||
|
'Pragma': 'no-cache',
|
||||||
|
'sec-fetch-mode': 'navigate'
|
||||||
|
}
|
||||||
|
|
||||||
|
url = 'https://www.iconfont.cn/api/project/download.zip?spm=a313x.manage_type_myprojects.i1.d7543c303.125e3a81q6VMOU&pid=4440655&ctoken=Ku-GfnHTFQU6ObMjjX4rrwYn'
|
||||||
|
res = r.get(url, headers=headers)
|
||||||
|
|
||||||
|
if res.status_code:
|
||||||
|
with open('./scripts/tmp.zip', 'wb') as fp:
|
||||||
|
fp.write(res.content)
|
||||||
|
|
||||||
|
# 解压文件
|
||||||
|
with zipfile.ZipFile('./scripts/tmp.zip', 'r') as zipf:
|
||||||
|
zipf.extractall('./scripts/tmp')
|
||||||
|
|
||||||
|
# 将文件搬运至工作区,我的 css 全放在 public 下面了,你的视情况而定
|
||||||
|
for parent, _, files in os.walk('./scripts/tmp'):
|
||||||
|
for file in files:
|
||||||
|
filepath = os.path.join(parent, file)
|
||||||
|
if file.startswith('demo'):
|
||||||
|
continue
|
||||||
|
if file.endswith('.css'):
|
||||||
|
content = open(filepath, 'r', encoding='utf-8').read().replace('font-size: 16px;', '')
|
||||||
|
open(filepath, 'w', encoding='utf-8').write(content)
|
||||||
|
shutil.move(filepath, os.path.join('./public', file))
|
||||||
|
elif file.endswith('.woff2'):
|
||||||
|
shutil.move(filepath, os.path.join('./public', file))
|
||||||
|
|
||||||
|
# 删除压缩包和解压区域
|
||||||
|
os.remove('./scripts/tmp.zip')
|
||||||
|
shutil.rmtree('./scripts/tmp')
|
27
scripts/update_icon_from_server.py
Normal file
27
scripts/update_icon_from_server.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import requests as r
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
res = r.get('https://kirigaya.cn/files/links/tmp.zip')
|
||||||
|
|
||||||
|
# 解压文件
|
||||||
|
with zipfile.ZipFile('./scripts/tmp.zip', 'r') as zipf:
|
||||||
|
zipf.extractall('./scripts/tmp')
|
||||||
|
|
||||||
|
# 将文件搬运至工作区,我的 css 全放在 public 下面了,你的视情况而定
|
||||||
|
for parent, _, files in os.walk('./scripts/tmp'):
|
||||||
|
for file in files:
|
||||||
|
filepath = os.path.join(parent, file)
|
||||||
|
if file.startswith('demo'):
|
||||||
|
continue
|
||||||
|
if file.endswith('.css'):
|
||||||
|
content = open(filepath, 'r', encoding='utf-8').read().replace('font-size: 16px;', '')
|
||||||
|
open(filepath, 'w', encoding='utf-8').write(content)
|
||||||
|
shutil.move(filepath, os.path.join('./public', file))
|
||||||
|
elif file.endswith('.woff2'):
|
||||||
|
shutil.move(filepath, os.path.join('./public', file))
|
||||||
|
|
||||||
|
# 删除压缩包和解压区域
|
||||||
|
os.remove('./scripts/tmp.zip')
|
||||||
|
shutil.rmtree('./scripts/tmp')
|
13
src/App.vue
13
src/App.vue
@ -1,14 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<!-- 上方的 toolbar -->
|
||||||
|
<ToolBar></ToolBar>
|
||||||
|
|
||||||
<!-- 主渲染区 -->
|
<!-- 主渲染区 -->
|
||||||
<MainRender></MainRender>
|
<MainRender></MainRender>
|
||||||
|
|
||||||
<!-- 左上角的 sidebar,用于显示 -->
|
<!-- 左上角的 sidebar,用于显示 -->
|
||||||
<Sidebar></Sidebar>
|
<Sidebar></Sidebar>
|
||||||
|
|
||||||
<!-- 工具栏 -->
|
|
||||||
<div class="vcd-toolbar">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 显示当前信号树形关系 -->
|
<!-- 显示当前信号树形关系 -->
|
||||||
<!-- 右侧工具合集 -->
|
<!-- 右侧工具合集 -->
|
||||||
<RightNav :topModules="VcdInfo.topModules"></RightNav>
|
<RightNav :topModules="VcdInfo.topModules"></RightNav>
|
||||||
@ -24,6 +23,8 @@ import { emitter, globalLookup, globalSetting } from '@/hook/global';
|
|||||||
import { makeWaveView } from '@/hook/render';
|
import { makeWaveView } from '@/hook/render';
|
||||||
import { getCrossOriginWorkerURL } from '@/hook/network';
|
import { getCrossOriginWorkerURL } from '@/hook/network';
|
||||||
|
|
||||||
|
|
||||||
|
import ToolBar from '@/components/toolbar';
|
||||||
import Sidebar from '@/components/sidebar';
|
import Sidebar from '@/components/sidebar';
|
||||||
import RightNav from '@/components/right-nav.vue';
|
import RightNav from '@/components/right-nav.vue';
|
||||||
import MainRender from '@/components/render';
|
import MainRender from '@/components/render';
|
||||||
@ -73,6 +74,7 @@ onMounted(async () => {
|
|||||||
document.body.style.setProperty('--time-scale-height', '50px');
|
document.body.style.setProperty('--time-scale-height', '50px');
|
||||||
document.body.style.setProperty('--vcd-render-padding', '30px');
|
document.body.style.setProperty('--vcd-render-padding', '30px');
|
||||||
document.body.style.setProperty('--sidebar-width', '230px');
|
document.body.style.setProperty('--sidebar-width', '230px');
|
||||||
|
document.body.style.setProperty('--toolbar-height', '60px');
|
||||||
|
|
||||||
// signal height
|
// signal height
|
||||||
document.body.style.setProperty('--display-signal-info-height', globalSetting.displaySignalHeight + 'px');
|
document.body.style.setProperty('--display-signal-info-height', globalSetting.displaySignalHeight + 'px');
|
||||||
@ -113,7 +115,7 @@ onMounted(async () => {
|
|||||||
globalLookup.chango = signalValues;
|
globalLookup.chango = signalValues;
|
||||||
makeWaveView();
|
makeWaveView();
|
||||||
|
|
||||||
console.log(signalValues['*']);
|
// console.log(signalValues['*']);
|
||||||
|
|
||||||
// console.log('duration time', globalLookup.time);
|
// console.log('duration time', globalLookup.time);
|
||||||
emitter.emit('meta-ready', null);
|
emitter.emit('meta-ready', null);
|
||||||
@ -144,4 +146,5 @@ onMounted(async () => {
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -52,6 +52,7 @@ export default {
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
transform: translateY(var(--toolbar-height));
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-vline {
|
.vcd-vline {
|
||||||
@ -92,6 +93,7 @@ export default {
|
|||||||
|
|
||||||
.vcd-values text {
|
.vcd-values text {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
font-family: var(--vcd-value-font-family);
|
||||||
text-anchor: middle;
|
text-anchor: middle;
|
||||||
fill: #fff;
|
fill: #fff;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="setting-wrapper">
|
<div class="setting-wrapper">
|
||||||
<div class="setting-container">
|
<el-scrollbar height="98vh">
|
||||||
<div class="setting-section">
|
<div class="setting-section">
|
||||||
<h2>{{ t('general-setting') }}</h2>
|
<h2>{{ t('general-setting') }}</h2>
|
||||||
<div class="setting-option" style="width: 300px;">
|
<div class="setting-option" style="width: 220px;">
|
||||||
<span class="option-title">{{ t('language-setting') }}</span>
|
<span class="option-title">{{ t('language-setting') }}</span>
|
||||||
<el-select name="language-setting" class="language-setting" v-model="locale">
|
<div style="width: 100px;">
|
||||||
|
<el-select
|
||||||
|
name="language-setting"
|
||||||
|
class="language-setting"
|
||||||
|
v-model="locale"
|
||||||
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="option in languageSetting.options"
|
v-for="option in languageSetting.options"
|
||||||
:value="option.value"
|
:value="option.value"
|
||||||
@ -14,8 +19,9 @@
|
|||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="setting-option">
|
<!-- <div class="setting-option">
|
||||||
<span class="option-title">
|
<span class="option-title">
|
||||||
{{ t('prerender') }}
|
{{ t('prerender') }}
|
||||||
<help-icon placement="left"
|
<help-icon placement="left"
|
||||||
@ -25,7 +31,7 @@
|
|||||||
<el-switch v-model="globalSetting.prerender" size="default"/>
|
<el-switch v-model="globalSetting.prerender" size="default"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br> -->
|
||||||
|
|
||||||
<div class="setting-option">
|
<div class="setting-option">
|
||||||
<span class="option-title">
|
<span class="option-title">
|
||||||
@ -89,7 +95,8 @@
|
|||||||
<br>
|
<br>
|
||||||
|
|
||||||
<div class="setting-option">
|
<div class="setting-option">
|
||||||
<span class="option-title">{{ t('wavecolor') }}</span>
|
<span class="option-title" style="width: 100px;">{{ t('wavecolor') }}</span>
|
||||||
|
<div style="width: 120px">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="wavecolor.currentOptionIndex"
|
v-model="wavecolor.currentOptionIndex"
|
||||||
collapse-tags
|
collapse-tags
|
||||||
@ -99,6 +106,7 @@
|
|||||||
<el-option v-for="option in wavecolor.options" :key="option.value"
|
<el-option v-for="option in wavecolor.options" :key="option.value"
|
||||||
:label="option.label" :value="option.value" />
|
:label="option.label" :value="option.value" />
|
||||||
</el-select>
|
</el-select>
|
||||||
|
</div>
|
||||||
<div style="height: 20px; width: 20px;"></div>
|
<div style="height: 20px; width: 20px;"></div>
|
||||||
<el-color-picker
|
<el-color-picker
|
||||||
v-model="wavecolor.colors[wavecolor.currentOptionIndex]"
|
v-model="wavecolor.colors[wavecolor.currentOptionIndex]"
|
||||||
@ -129,8 +137,10 @@
|
|||||||
|
|
||||||
<div class="setting-option">
|
<div class="setting-option">
|
||||||
<span class="option-title">{{ t('search-scope') }}</span>
|
<span class="option-title">{{ t('search-scope') }}</span>
|
||||||
|
<div style="width: 150px;">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="globalSetting.searchScope"
|
v-model="globalSetting.searchScope"
|
||||||
|
size="large"
|
||||||
multiple
|
multiple
|
||||||
collapse-tags
|
collapse-tags
|
||||||
collapse-tags-tooltip
|
collapse-tags-tooltip
|
||||||
@ -143,8 +153,8 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -204,6 +214,7 @@ watch(() => wavecolor.currentOptionIndex, () => {
|
|||||||
console.log(wavecolor.currentOptionIndex);
|
console.log(wavecolor.currentOptionIndex);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 更新默认波形颜色
|
||||||
watch(() => wavecolor.colors, () => {
|
watch(() => wavecolor.colors, () => {
|
||||||
const colorString = wavecolor.colors[wavecolor.currentOptionIndex];
|
const colorString = wavecolor.colors[wavecolor.currentOptionIndex];
|
||||||
const rgba = rgba2WebglColor(colorString);
|
const rgba = rgba2WebglColor(colorString);
|
||||||
@ -212,7 +223,8 @@ watch(() => wavecolor.colors, () => {
|
|||||||
const waveRender = globalLookup.getWaveRender();
|
const waveRender = globalLookup.getWaveRender();
|
||||||
const glColorIndex = wavecolor.glColorMap[currentOption.value];
|
const glColorIndex = wavecolor.glColorMap[currentOption.value];
|
||||||
const colorUpdaters = [{ index: glColorIndex, rgba }];
|
const colorUpdaters = [{ index: glColorIndex, rgba }];
|
||||||
if (glColorIndex === 2) { // 默认情况下, value = 0 和 value = 1 保持一致
|
if (glColorIndex === 2) {
|
||||||
|
// 默认情况下, value = 0 和 value = 1 保持一致
|
||||||
colorUpdaters.push({ index: 3, rgba });
|
colorUpdaters.push({ index: 3, rgba });
|
||||||
}
|
}
|
||||||
waveRender.updateGLColor(colorUpdaters, { updateMask: true });
|
waveRender.updateGLColor(colorUpdaters, { updateMask: true });
|
||||||
|
30
src/components/sidebar/color-picker.js
Normal file
30
src/components/sidebar/color-picker.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { gl_Colors } from '@/hook/wave-view/render-utils';
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
|
||||||
|
// 用于管理当前信号 link 和对应颜色索引的映射关系
|
||||||
|
// 如果 link 不在下表中,则使用默认的颜色索引
|
||||||
|
// 默认颜色请参考 render-utils.js 中有关 gl_Colors_template 的定义
|
||||||
|
export const userSignalColorMap = new Map();
|
||||||
|
|
||||||
|
// 当前选中波形目前的颜色
|
||||||
|
export const colorPickerManage = reactive({
|
||||||
|
currentSelecteIndex: -1
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {WireItem} signal
|
||||||
|
*/
|
||||||
|
export function updateColorPickerManage(signal) {
|
||||||
|
if (userSignalColorMap.has(signal.link)) {
|
||||||
|
colorPickerManage.currentSelecteIndex = userSignalColorMap.get(signal.link);
|
||||||
|
} else {
|
||||||
|
// 找到默认的配色
|
||||||
|
if (signal.size === 1) {
|
||||||
|
colorPickerManage.currentSelecteIndex = 2;
|
||||||
|
} else {
|
||||||
|
colorPickerManage.currentSelecteIndex = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
115
src/components/sidebar/color-picker.vue
Normal file
115
src/components/sidebar/color-picker.vue
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<template>
|
||||||
|
<div class="color-picker" :style="colorPickerStyle">
|
||||||
|
<div
|
||||||
|
class="color-option"
|
||||||
|
v-for="item in colorPickerStyles"
|
||||||
|
:key="item.index"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
:style="item.style"
|
||||||
|
class="color-option-inner"
|
||||||
|
@click="handleColorSelector(item.index)"
|
||||||
|
>
|
||||||
|
<transition name="main-fade">
|
||||||
|
<span
|
||||||
|
v-show="colorPickerManage.currentSelecteIndex === item.index"
|
||||||
|
class="choose iconfont icon-choose"
|
||||||
|
></span>
|
||||||
|
</transition>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { gl_Colors, gl_Colors_template } from '@/hook/wave-view/render-utils';
|
||||||
|
import { reactive, computed, defineComponent } from 'vue';
|
||||||
|
import { contextmenu } from './handle-contextmenu';
|
||||||
|
import { colorPickerManage, userSignalColorMap } from './color-picker';
|
||||||
|
import { globalLookup } from '@/hook/global';
|
||||||
|
|
||||||
|
defineComponent({ name: 'color-picker' });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ColorPickerOption
|
||||||
|
* @property {number} index
|
||||||
|
* @property {string} style
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 从 gl_Colors_template 的 11 开始就是自定义颜色了
|
||||||
|
* @type {ColorPickerOption[]}
|
||||||
|
*/
|
||||||
|
const colorPickerStyles = [];
|
||||||
|
|
||||||
|
for (let i = 1; i < gl_Colors_template.length; ++ i) {
|
||||||
|
const rgba = gl_Colors_template[i];
|
||||||
|
const styleString = `background-color: rgba(${rgba[0]},${rgba[1]},${rgba[2]},${rgba[3]})`;
|
||||||
|
colorPickerStyles.push({
|
||||||
|
index: i,
|
||||||
|
style: styleString
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handleColorSelector(index) {
|
||||||
|
colorPickerManage.currentSelecteIndex = index;
|
||||||
|
if (contextmenu.currentWire) {
|
||||||
|
const link = contextmenu.currentWire.link;
|
||||||
|
userSignalColorMap.set(link, index);
|
||||||
|
// 更换颜色需要重置 波形 渲染的 VAO
|
||||||
|
globalLookup.waveRender.renderPipeHook.userDefinedColor = index;
|
||||||
|
globalLookup.waveRender.resetVertice(link);
|
||||||
|
globalLookup.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const colorPickerStyle = computed(() => ({
|
||||||
|
top: '0px',
|
||||||
|
left: contextmenu.width + 'px'
|
||||||
|
}));
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.color-picker {
|
||||||
|
width: 280px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: row;
|
||||||
|
position: absolute;
|
||||||
|
padding: 10px;
|
||||||
|
box-shadow: 0 0 10px 1px rgb(16, 16, 16);
|
||||||
|
background-color: var(--sidebar);
|
||||||
|
border: solid 1px var(--sidebar-border);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-picker .color-option {
|
||||||
|
display: flex;
|
||||||
|
border-radius: .7em;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
margin: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 2px solid var(--sidebar);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-picker .color-option > span {
|
||||||
|
border-radius: .5em;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option-inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 20px;
|
||||||
|
color: var(--sidebar);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
18
src/components/sidebar/exist-group.js
Normal file
18
src/components/sidebar/exist-group.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { globalLookup } from "@/hook/global";
|
||||||
|
import { reactive } from "vue";
|
||||||
|
|
||||||
|
// 从 globalLookup.currentWiresRenderView 中可以获取当前的分组情况
|
||||||
|
export function getCurrentGroups() {
|
||||||
|
const groups = globalLookup.currentWiresRenderView.filter(view => view.renderType === 1);
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const existGroup = reactive({
|
||||||
|
display: false,
|
||||||
|
show() {
|
||||||
|
this.display = true;
|
||||||
|
},
|
||||||
|
hide() {
|
||||||
|
this.display = false;
|
||||||
|
}
|
||||||
|
});
|
130
src/components/sidebar/exist-group.vue
Normal file
130
src/components/sidebar/exist-group.vue
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<template>
|
||||||
|
<div class="exist-group" :style="existGroupStyle">
|
||||||
|
<div class="wrapper">
|
||||||
|
<div
|
||||||
|
v-if="currentGroups.length > 0"
|
||||||
|
v-for="(item, index) in currentGroups"
|
||||||
|
:key="index"
|
||||||
|
class="group-item"
|
||||||
|
:style="getItemStyle(item)"
|
||||||
|
>
|
||||||
|
<span class="iconfont icon-group"></span>
|
||||||
|
 
|
||||||
|
<span>
|
||||||
|
{{ getItemName(item) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-else
|
||||||
|
class="empty-status"
|
||||||
|
>
|
||||||
|
<span class="iconfont icon-empty"></span>
|
||||||
|
|
||||||
|
<span>{{ t('context-menu.group.empty') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, defineComponent } from 'vue';
|
||||||
|
import { contextmenu } from './handle-contextmenu';
|
||||||
|
import { currentGroups } from './manage-group';
|
||||||
|
import { globalLookup } from '@/hook/global';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
defineComponent({ name: 'exist-group' });
|
||||||
|
|
||||||
|
const existGroupStyle = computed(() => ({
|
||||||
|
top: '0px',
|
||||||
|
left: contextmenu.width + 'px'
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {WaveRenderSidebarItem} view
|
||||||
|
*/
|
||||||
|
function getItemStyle(view) {
|
||||||
|
return `background-color: ${view.groupInfo.color}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {WaveRenderSidebarItem} view
|
||||||
|
*/
|
||||||
|
function getItemName(view) {
|
||||||
|
let name = t('context-menu.group.uname-group');
|
||||||
|
if (view.groupInfo.name) {
|
||||||
|
name = view.groupInfo.name;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.exist-group {
|
||||||
|
color: var(--foreground);
|
||||||
|
width: 260px;
|
||||||
|
position: absolute;
|
||||||
|
padding: 10px;
|
||||||
|
box-shadow: 0 0 10px 1px rgb(16, 16, 16);
|
||||||
|
background-color: var(--sidebar);
|
||||||
|
border: solid 1px var(--sidebar-border);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
cursor: default;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-item {
|
||||||
|
width: 150px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--sidebar);
|
||||||
|
border-radius: .5em;
|
||||||
|
height: 20px;
|
||||||
|
padding: 5px;
|
||||||
|
margin: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--animation-5s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-item > span {
|
||||||
|
max-width: 80%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-item:hover {
|
||||||
|
width: 200px;
|
||||||
|
transition: var(--animation-5s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-item .iconfont {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-status {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-status .iconfont {
|
||||||
|
font-size: 70px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-status > span {
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
168
src/components/sidebar/group-context-menu.vue
Normal file
168
src/components/sidebar/group-context-menu.vue
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
<template>
|
||||||
|
<div class="group-context-menu">
|
||||||
|
<div class="top-region">
|
||||||
|
<div class="group-name-input">
|
||||||
|
<el-input
|
||||||
|
:ref="el => groupcontextmenu.groupNameInput = el"
|
||||||
|
v-model="value"
|
||||||
|
size="default"
|
||||||
|
></el-input>
|
||||||
|
</div>
|
||||||
|
<div class="group-color-selector">
|
||||||
|
<span
|
||||||
|
v-for="(item, index) in groupColors"
|
||||||
|
:key="index"
|
||||||
|
:style="makeColorStyle(item.color)"
|
||||||
|
class="item"
|
||||||
|
@click="onClick(item)"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="focus-circle"
|
||||||
|
:class="{'active': item.color === currentGroupColor}"
|
||||||
|
></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="group-function-list">
|
||||||
|
<div class="item"
|
||||||
|
@click="cancelGroup()"
|
||||||
|
>
|
||||||
|
<span class="iconfont icon-cancel-group"></span>
|
||||||
|
<span>{{ t('context-menu.group.cancel') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="item"
|
||||||
|
@click="deleteGroup()"
|
||||||
|
>
|
||||||
|
<span class="iconfont icon-delete-group"></span>
|
||||||
|
<span>{{ t('context-menu.group.delete') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { cancelGroup, deleteGroup, groupColors, makeColorStyle } from './manage-group';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { contextmenu, groupcontextmenu } from './handle-contextmenu';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const currentGroupColor = computed(() => {
|
||||||
|
if (groupcontextmenu.currentGroupView) {
|
||||||
|
return groupcontextmenu.currentGroupView.groupInfo.color;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
})
|
||||||
|
|
||||||
|
const value = computed({
|
||||||
|
get() {
|
||||||
|
if (groupcontextmenu.currentGroupView) {
|
||||||
|
return groupcontextmenu.currentGroupView.groupInfo.name;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set(v) {
|
||||||
|
if (groupcontextmenu.currentGroupView) {
|
||||||
|
groupcontextmenu.currentGroupView.groupInfo.name = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function onClick(item) {
|
||||||
|
groupcontextmenu.currentGroupView.groupInfo.color = item.color;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.group-context-menu {
|
||||||
|
z-index: 200;
|
||||||
|
border-radius: .5em;
|
||||||
|
padding: 10px;
|
||||||
|
position: fixed;
|
||||||
|
box-shadow: 0 0 10px 1px rgb(16, 16, 16);
|
||||||
|
background-color: var(--sidebar);
|
||||||
|
border: solid 1px var(--sidebar-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-context-menu .top-region {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-name-input {
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-color-selector {
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-color-selector .item {
|
||||||
|
border-radius: 99em;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-color-selector .item .focus-circle {
|
||||||
|
border-radius: 99em;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-color-selector .item .focus-circle.active {
|
||||||
|
border: 2px solid var(--sidebar);
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-function-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-function-list .item {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 5px;
|
||||||
|
padding: 3px;
|
||||||
|
padding-left: 10px;
|
||||||
|
border-radius: .5em;
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
cursor: pointer;
|
||||||
|
width: 90%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-function-list .item .iconfont {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
width: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-function-list .item:hover {
|
||||||
|
color: var(--sidebar);
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
#7CA532 25%,
|
||||||
|
rgba(200, 200, 200, 1) 37%,
|
||||||
|
rgba(255, 255, 255, 0) 63%
|
||||||
|
);
|
||||||
|
background-size: 400% 100%;
|
||||||
|
animation: loading-mask 1.5s cubic-bezier(0.23,1,0.32,1);
|
||||||
|
-webkit-animation: loading-mask 1.5s cubic-bezier(0.23,1,0.32,1);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
131
src/components/sidebar/group-item.vue
Normal file
131
src/components/sidebar/group-item.vue
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<template>
|
||||||
|
<div class="group-item" :style="groupItemStyle">
|
||||||
|
<div
|
||||||
|
class="group-card"
|
||||||
|
:style="groupCardStyle"
|
||||||
|
@click="handleGroupClick(view)"
|
||||||
|
@contextmenu.prevent="handleGroupContextmenu($event, view, index)"
|
||||||
|
>
|
||||||
|
<span class="iconfont icon-group"></span>
|
||||||
|
 
|
||||||
|
<span>
|
||||||
|
{{ props.view.groupInfo.name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<VueDraggable
|
||||||
|
v-show="!props.view.groupInfo.collapse"
|
||||||
|
class="group-children"
|
||||||
|
v-model="props.view.children"
|
||||||
|
group="vcd-render-item"
|
||||||
|
:animation="150"
|
||||||
|
:on-update="onUpdate()"
|
||||||
|
ghost-class="signal-drag-ghost-class"
|
||||||
|
drag-class="signal-drag-drag-class"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="view in props.view.children"
|
||||||
|
:key="view.signalInfo.link"
|
||||||
|
:style="groupChildStyle"
|
||||||
|
>
|
||||||
|
<SignalItem
|
||||||
|
:view="view"
|
||||||
|
@click="handleItemClick(view.signalInfo.link)"
|
||||||
|
@contextmenu.prevent="handleContextmenu($event, view, null)"
|
||||||
|
></SignalItem>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</VueDraggable>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { globalLookup, globalStyle } from '@/hook/global';
|
||||||
|
import { defineComponent, computed, onMounted, reactive } from 'vue';
|
||||||
|
import { onUpdate } from './handle-drag';
|
||||||
|
|
||||||
|
import { VueDraggable } from 'vue-draggable-plus';
|
||||||
|
import SignalItem from './signal-item.vue';
|
||||||
|
import GroupContextMenu from './group-context-menu.vue';
|
||||||
|
import { groupcontextmenu, handleContextmenu, handleGroupClick, handleGroupContextmenu } from './handle-contextmenu';
|
||||||
|
|
||||||
|
defineComponent({ name: 'group-item' });
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
view: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const groupCardStyle = computed(() => ({
|
||||||
|
backgroundColor: props.view.groupInfo.color,
|
||||||
|
marginBottom: globalStyle.sideBarItemMargin + 'px',
|
||||||
|
borderBottomRightRadius: props.view.groupInfo.collapse ? '.5em' : '0em',
|
||||||
|
borderLeft: props.view.groupInfo.collapse ? '3px solid ' + props.view.groupInfo.color : '0'
|
||||||
|
}));
|
||||||
|
|
||||||
|
const groupItemStyle = computed(() => ({
|
||||||
|
borderLeft: props.view.groupInfo.collapse ? '0' : '3px solid ' + props.view.groupInfo.color
|
||||||
|
}));
|
||||||
|
|
||||||
|
const groupChildStyle = computed(() => ({
|
||||||
|
marginBottom: globalStyle.sideBarItemMargin + 'px'
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 刚刚挂载后,展示对应的 context menu
|
||||||
|
|
||||||
|
function handleItemClick(link) {
|
||||||
|
if (globalLookup.sidebarSelectedWireLinks.has(link)) {
|
||||||
|
globalLookup.sidebarSelectedWireLinks.delete(link);
|
||||||
|
} else {
|
||||||
|
globalLookup.sidebarSelectedWireLinks.add(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.group-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
border-top-left-radius: .7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-card {
|
||||||
|
max-width: calc(var(--sidebar-width) - 36px);
|
||||||
|
width: fit-content;
|
||||||
|
color: var(--sidebar);
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
height: var(--display-signal-info-height);
|
||||||
|
border-top-left-radius: .4em;
|
||||||
|
border-top-right-radius: .5em;
|
||||||
|
font-size: 1.0rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-card > span {
|
||||||
|
max-width: 83%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-children {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: 5px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-card .iconfont {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
255
src/components/sidebar/handle-contextmenu.js
Normal file
255
src/components/sidebar/handle-contextmenu.js
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
import { globalLookup } from "@/hook/global";
|
||||||
|
import { computed, nextTick, reactive } from "vue";
|
||||||
|
import { updateColorPickerManage } from "./color-picker";
|
||||||
|
import { WaveContainerView } from "@/hook/wave-container-view";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace contextmenu
|
||||||
|
*/
|
||||||
|
export const contextmenu = reactive({
|
||||||
|
show: false,
|
||||||
|
width: 300,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {WaveRenderSidebarItem | undefined}
|
||||||
|
*/
|
||||||
|
currentView: undefined,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {WireItem | undefined}
|
||||||
|
*/
|
||||||
|
currentWire: undefined,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
currentWireIndex: -1,
|
||||||
|
|
||||||
|
isSameSignal(view) {
|
||||||
|
if (this.currentWire) {
|
||||||
|
return this.currentWire.link === view.signalInfo.link;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const contextmenuStyle = computed(() => ({
|
||||||
|
left: contextmenu.x + 'px',
|
||||||
|
top: contextmenu.y + 'px'
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {MouseEvent} event
|
||||||
|
* @param {WaveRenderSidebarItem} view
|
||||||
|
* @param {number | null} index 为 null 说明 handleContextmenu 在 group 内部被调用
|
||||||
|
*/
|
||||||
|
export function handleContextmenu(event, view, index) {
|
||||||
|
groupcontextmenu.show = false;
|
||||||
|
contextmenu.x = event.x + 10 + window.scrollX;
|
||||||
|
contextmenu.y = event.clientY - 10 + window.scrollY;
|
||||||
|
const realSignal = globalLookup.link2CurrentWires.get(view.signalInfo.link);
|
||||||
|
|
||||||
|
contextmenu.currentView = view;
|
||||||
|
contextmenu.currentWire = realSignal;
|
||||||
|
contextmenu.show = true;
|
||||||
|
|
||||||
|
updateColorPickerManage(realSignal);
|
||||||
|
if (index !== null) {
|
||||||
|
contextmenu.currentWireIndex = index;
|
||||||
|
groupcontextmenu.updateHook = updateCurrentGroupContextMenu(event, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 延时 300 ms 是因为这里 menu 出现会使用 一个 0.35 秒的入场动画
|
||||||
|
setTimeout(() => {
|
||||||
|
getContextMenuWidth();
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 将当前 signal-item 相连的 group-item 更新进 groupcontextmenu.currentGroupCardEl
|
||||||
|
* @param {MouseEvent} event
|
||||||
|
* @param {WaveRenderSidebarItem} view
|
||||||
|
* @param {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
async function updateCurrentGroupContextMenu(event, view) {
|
||||||
|
const node = event.target;
|
||||||
|
const container = findSignalItemEl(node);
|
||||||
|
if (container) {
|
||||||
|
const groupItem = container.nextSibling;
|
||||||
|
groupcontextmenu.currentGroupView = view;
|
||||||
|
groupcontextmenu.currentGroupCardEl = groupItem.childNodes[0];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {HTMLElement | undefined} node
|
||||||
|
* @returns {HTMLElement | undefined}
|
||||||
|
*/
|
||||||
|
function findSignalItemEl(node) {
|
||||||
|
const maxFindTimes = 10;
|
||||||
|
for (let i = 0; ; ++ i) {
|
||||||
|
if (i >= maxFindTimes || node === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (node.className.startsWith('signal-item')) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
node = node.parentNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContextMenuWidth() {
|
||||||
|
let width = 300;
|
||||||
|
const menus = document.querySelectorAll('.sidebar-context-menu');
|
||||||
|
if (menus) {
|
||||||
|
const menu = menus[0];
|
||||||
|
width = menu.offsetWidth || menu.clientWidth;
|
||||||
|
}
|
||||||
|
contextmenu.width = width;
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 用于魔改取色器的
|
||||||
|
export const colorPicker = reactive({
|
||||||
|
display: false,
|
||||||
|
show() {
|
||||||
|
this.display = true;
|
||||||
|
},
|
||||||
|
hide() {
|
||||||
|
this.display = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const groupcontextmenu = reactive({
|
||||||
|
show: false,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {WaveRenderSidebarItem | undefined}
|
||||||
|
*/
|
||||||
|
currentGroupView: undefined,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type
|
||||||
|
*/
|
||||||
|
currentGroupCardEl: null,
|
||||||
|
groupNameInput: null,
|
||||||
|
updateHook: null
|
||||||
|
});
|
||||||
|
|
||||||
|
export const groupcontextmenuStyle = computed(() => ({
|
||||||
|
left: groupcontextmenu.x + 'px',
|
||||||
|
top: groupcontextmenu.y + 'px'
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {WaveRenderSidebarItem} view
|
||||||
|
*/
|
||||||
|
export function handleGroupClick(view) {
|
||||||
|
view.groupInfo.collapse = !view.groupInfo.collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {HTMLElement | undefined} node
|
||||||
|
* @returns {HTMLElement | undefined}
|
||||||
|
*/
|
||||||
|
function findGroupCardEl(node) {
|
||||||
|
const maxSearchTime = 3;
|
||||||
|
for (let i = 0;; ++ i) {
|
||||||
|
// 往外搜索最多三次,找到 group-card 这个类的 span
|
||||||
|
if (i >= maxSearchTime || node === undefined || node === null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (node.className.includes('group-card')) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
node = node.parentNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getScreenPosition(element) {
|
||||||
|
let x = 0;
|
||||||
|
let y = 0;
|
||||||
|
|
||||||
|
// 遍历元素及其所有祖先元素
|
||||||
|
while (element) {
|
||||||
|
x += element.offsetLeft; // 累加 offsetLeft
|
||||||
|
y += element.offsetTop; // 累加 offsetTop
|
||||||
|
element = element.offsetParent; // 移动到下一个祖先元素
|
||||||
|
}
|
||||||
|
|
||||||
|
// 考虑到页面滚动
|
||||||
|
x -= window.pageXOffset;
|
||||||
|
y -= window.pageYOffset;
|
||||||
|
|
||||||
|
return { x: x, y: y };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {MouseEvent} event
|
||||||
|
* @param {WaveRenderSidebarItem} view
|
||||||
|
* @param {number} index
|
||||||
|
*/
|
||||||
|
export function handleGroupContextmenu(event, view, index) {
|
||||||
|
contextmenu.show = false;
|
||||||
|
const node = event.target;
|
||||||
|
const groupCard = findGroupCardEl(node);
|
||||||
|
if (groupCard) {
|
||||||
|
const {x, y} = getScreenPosition(groupCard);
|
||||||
|
groupcontextmenu.x = x + groupCard.offsetWidth + 10;
|
||||||
|
groupcontextmenu.y = y;
|
||||||
|
|
||||||
|
groupcontextmenu.currentGroupView = view;
|
||||||
|
groupcontextmenu.show = true;
|
||||||
|
|
||||||
|
if (groupcontextmenu.groupNameInput) {
|
||||||
|
groupcontextmenu.groupNameInput.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function handleGroupContextmenuFromSignalItem() {
|
||||||
|
contextmenu.show = false;
|
||||||
|
|
||||||
|
if (groupcontextmenu.updateHook) {
|
||||||
|
// const ok = await groupcontextmenu.updateHook;
|
||||||
|
|
||||||
|
groupcontextmenu.x = contextmenu.x + 10 + window.scrollX;
|
||||||
|
groupcontextmenu.y = contextmenu.y - 5 + window.scrollY;
|
||||||
|
groupcontextmenu.show = true;
|
||||||
|
|
||||||
|
if (groupcontextmenu.groupNameInput) {
|
||||||
|
groupcontextmenu.groupNameInput.focus();
|
||||||
|
}
|
||||||
|
// if (ok && groupcontextmenu.currentGroupCardEl) {
|
||||||
|
// // const groupCard = groupcontextmenu.currentGroupCardEl;
|
||||||
|
// // const {x, y} = getScreenPosition(groupCard);
|
||||||
|
|
||||||
|
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function deleteSignalByView() {
|
||||||
|
const signal = contextmenu.currentWire;
|
||||||
|
if (signal) {
|
||||||
|
WaveContainerView.delete(signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
contextmenu.show = false;
|
||||||
|
}
|
7
src/components/sidebar/handle-drag.js
Normal file
7
src/components/sidebar/handle-drag.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { globalLookup } from "@/hook/global";
|
||||||
|
import { updateCurrentGroups } from "./manage-group";
|
||||||
|
|
||||||
|
export function onUpdate() {
|
||||||
|
globalLookup.render();
|
||||||
|
updateCurrentGroups();
|
||||||
|
}
|
@ -1,85 +1,84 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="vcd-sidebar-wrapper">
|
<div class="vcd-sidebar-wrapper"
|
||||||
|
@click="handleSidebarGlobalClick"
|
||||||
|
>
|
||||||
<div class="display-signal-wrapper" :style="sideBarWrapperStyle">
|
<div class="display-signal-wrapper" :style="sideBarWrapperStyle">
|
||||||
<div class="display-signal-container" :style="sidedBarContainerStyle">
|
<div class="display-signal-container" :style="sidedBarContainerStyle">
|
||||||
<div :style="timeScaleStyle"></div>
|
<div :style="timeScaleStyle"></div>
|
||||||
<VueDraggable
|
<VueDraggable
|
||||||
v-model="globalLookup.currentWires"
|
v-model="globalLookup.currentWiresRenderView"
|
||||||
group="vcd-render-item"
|
:group="{ name: 'vcd-render-item' }"
|
||||||
:animation="150"
|
:animation="150"
|
||||||
ghost-class="signal-drag-ghost"
|
:on-update="onUpdate()"
|
||||||
|
ghost-class="signal-drag-ghost-class"
|
||||||
|
drag-class="signal-drag-drag-class"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="display-signal-item-container"
|
class="display-signal-item-container"
|
||||||
:style="sideBarItemStyle"
|
:style="sideBarItemStyle"
|
||||||
v-for="(signal, index) in globalLookup.currentWires"
|
v-for="(view, index) in globalLookup.currentWiresRenderView"
|
||||||
:key="signal.link"
|
:key="view.signalInfo.link"
|
||||||
>
|
>
|
||||||
<div class="display-signal-item">
|
|
||||||
<div class="signal-color-vendor">
|
|
||||||
<span :class="makeSignalIconClass(signal)"></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="signal-info">
|
<SignalItem
|
||||||
<div class="signal-info">
|
v-show="view.renderType === 0"
|
||||||
<el-tooltip
|
:view="view"
|
||||||
effect="dark"
|
@click="handleItemClick(view.signalInfo.link)"
|
||||||
:content="makeFullSignalNameDeps(signal)"
|
@contextmenu.prevent="handleContextmenu($event, view, index)"
|
||||||
placement="top"
|
/>
|
||||||
raw-content
|
|
||||||
>
|
|
||||||
<div class="signal-info-name">
|
|
||||||
<span class="signal-parent-info" v-show="showParent">{{ signal.parent.name }}</span>
|
|
||||||
<span>{{ signal.name }}</span>
|
|
||||||
</div>
|
|
||||||
</el-tooltip>
|
|
||||||
|
|
||||||
<div class="signal-info-width" v-show="showWidth">
|
<GroupItem
|
||||||
<div :class="signal.size > 1 ? 'signal-info-caption' : ''">
|
v-show="view.renderType === 1"
|
||||||
{{ makeSignalCaption(signal) }}
|
:view="view"
|
||||||
</div>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="signal-info-current-value-wrapper">
|
|
||||||
<span></span>
|
|
||||||
<el-tooltip
|
|
||||||
effect="dark"
|
|
||||||
:content="globalLookup.currentSignalValues[signal.link] + ''"
|
|
||||||
placement="top"
|
|
||||||
raw-content
|
|
||||||
><div class="signal-info-current-value">
|
|
||||||
{{ globalLookup.currentSignalValues[signal.link] }}
|
|
||||||
</div></el-tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
</div>
|
||||||
</VueDraggable>
|
</VueDraggable>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="add-signal-button" @click="addSignal">
|
<!-- <div class="add-signal-button" @click="addSignal">
|
||||||
<span class="iconfont icon-collections"></span>
|
<span class="iconfont icon-collections"></span>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
|
||||||
|
<teleport to='body'>
|
||||||
|
<transition name="collapse-from-top" mode="out-in">
|
||||||
|
<SignalContextMenu
|
||||||
|
v-show="contextmenu.show"
|
||||||
|
:style="contextmenuStyle"
|
||||||
|
></SignalContextMenu>
|
||||||
|
</transition>
|
||||||
|
</teleport>
|
||||||
|
|
||||||
|
<teleport to='body'>
|
||||||
|
<transition name="collapse-from-top" mode="out-in">
|
||||||
|
<GroupContextMenu
|
||||||
|
v-show="groupcontextmenu.show"
|
||||||
|
:style="groupcontextmenuStyle"
|
||||||
|
></GroupContextMenu>
|
||||||
|
</transition>
|
||||||
|
</teleport>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, computed, defineComponent, ref } from 'vue';
|
import { reactive, computed, defineComponent } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { VueDraggable } from 'vue-draggable-plus';
|
import { VueDraggable } from 'vue-draggable-plus';
|
||||||
|
|
||||||
import { emitter, globalLookup, globalSetting, globalStyle } from '@/hook/global';
|
import { emitter, globalLookup, globalSetting, globalStyle } from '@/hook/global';
|
||||||
import { makeIconClass } from '@/hook/utils';
|
import { makeIconClass } from '@/hook/utils';
|
||||||
|
import { onUpdate } from './handle-drag';
|
||||||
|
import SignalContextMenu from './signal-context-menu.vue';
|
||||||
|
import { colorPicker, contextmenu, contextmenuStyle, groupcontextmenu, groupcontextmenuStyle, handleContextmenu } from './handle-contextmenu';
|
||||||
|
import SignalItem from './signal-item.vue';
|
||||||
|
import GroupItem from './group-item.vue';
|
||||||
|
import GroupContextMenu from './group-context-menu.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
defineComponent({ name: 'side-bar' });
|
defineComponent({ name: 'side-bar' });
|
||||||
|
|
||||||
|
|
||||||
const showWidth = computed(() => globalSetting.displaySignalInfoScope.includes('width'));
|
|
||||||
const showParent = computed(() => globalSetting.displaySignalInfoScope.includes('parent'));
|
|
||||||
|
|
||||||
const timeScaleStyle = computed(() => ({
|
const timeScaleStyle = computed(() => ({
|
||||||
height: `${globalStyle.timeScaleHeight}px`
|
height: `${globalStyle.timeScaleHeight}px`
|
||||||
}));
|
}));
|
||||||
@ -101,46 +100,35 @@ function addSignal() {
|
|||||||
emitter.emit('right-nav', 0);
|
emitter.emit('right-nav', 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeSignalIconClass(signal) {
|
/**
|
||||||
return 'iconfont ' + makeIconClass(signal);
|
*
|
||||||
|
* @param {WaveRenderSidebarItem} view
|
||||||
|
*/
|
||||||
|
function getVForKey(view) {
|
||||||
|
return view.signalInfo.link + '-' + view.renderType;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeSignalCaption(signal) {
|
|
||||||
return signal.size === 1 ? '' : `${signal.size - 1}:0`;
|
function handleItemClick(link) {
|
||||||
|
if (globalLookup.sidebarSelectedWireLinks.has(link)) {
|
||||||
|
globalLookup.sidebarSelectedWireLinks.delete(link);
|
||||||
|
} else {
|
||||||
|
globalLookup.sidebarSelectedWireLinks.add(link);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeFullSignalNameDeps(signal) {
|
|
||||||
const deps = [];
|
|
||||||
while (signal) {
|
|
||||||
if (signal.name && signal.type) {
|
|
||||||
deps.push(signal);
|
|
||||||
}
|
|
||||||
signal = signal.parent;
|
|
||||||
}
|
|
||||||
let htmlString = '';
|
|
||||||
for (let i = deps.length - 1; i >= 0; -- i) {
|
|
||||||
const mod = deps[i];
|
|
||||||
// const displayName = mod.name.length > 6 ? mod.name.substring(0, 6) + '...' : mod.name;
|
|
||||||
const iconClass = makeIconClass(mod);
|
|
||||||
const iconText = `<span class="iconfont ${iconClass}"></span> ${mod.name}`;
|
|
||||||
|
|
||||||
htmlString += iconText;
|
function handleSidebarGlobalClick() {
|
||||||
|
contextmenu.show = false;
|
||||||
if (i > 0) {
|
groupcontextmenu.show = false;
|
||||||
htmlString += '<div class="dep-arrow"></div>';
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
htmlString = '<div class="signal-info-tooltip-wrapper">' + htmlString + '</div>';
|
|
||||||
return htmlString;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.vcd-sidebar-wrapper {
|
.vcd-sidebar-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0px;
|
top: var(--toolbar-height);
|
||||||
left: 0px;
|
left: 0px;
|
||||||
z-index: 60;
|
z-index: 60;
|
||||||
}
|
}
|
||||||
@ -162,9 +150,10 @@ function makeFullSignalNameDeps(signal) {
|
|||||||
|
|
||||||
.display-signal-wrapper {
|
.display-signal-wrapper {
|
||||||
background-color: var(--sidebar);
|
background-color: var(--sidebar);
|
||||||
|
border-radius: 0 .8em .8em 0;
|
||||||
border: solid 1px var(--sidebar-border);
|
border: solid 1px var(--sidebar-border);
|
||||||
width: var(--sidebar-width);
|
width: var(--sidebar-width);
|
||||||
height: calc(100vh - 90px);
|
height: calc(100vh - var(--toolbar-height));
|
||||||
box-shadow: 0 0 15px 1px rgb(16, 16, 16);
|
box-shadow: 0 0 15px 1px rgb(16, 16, 16);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@ -176,6 +165,7 @@ function makeFullSignalNameDeps(signal) {
|
|||||||
display: flex;
|
display: flex;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
transition: var(--animation-5s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.display-signal-item-container {
|
.display-signal-item-container {
|
||||||
@ -184,7 +174,7 @@ function makeFullSignalNameDeps(signal) {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.display-signal-item::selection {
|
.signal-info-current-value-wrapper::selection {
|
||||||
background-color: none;
|
background-color: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +213,7 @@ function makeFullSignalNameDeps(signal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.signal-info-tooltip-wrapper {
|
.signal-info-tooltip-wrapper {
|
||||||
display: flex;
|
display: inline-block;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
}
|
}
|
||||||
@ -276,8 +266,28 @@ function makeFullSignalNameDeps(signal) {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.signal-drag-ghost {
|
.signal-drag-ghost-class .display-signal-item,
|
||||||
opacity: 0.5;
|
.signal-drag-ghost-class .signal-info-current-value-wrapper {
|
||||||
background: #c8ebfb;
|
color: var(--sidebar);
|
||||||
|
background-color: #7CA532 !important;
|
||||||
|
transition: var(--animation-5s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.active .display-signal-item,
|
||||||
|
.active .signal-info-current-value-wrapper {
|
||||||
|
background-color: var(--button-active);
|
||||||
|
transition: var(--animation-5s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-active .display-signal-item,
|
||||||
|
.right-active .signal-info-current-value-wrapper {
|
||||||
|
color: var(--sidebar);
|
||||||
|
background-color: #7CA532 !important;
|
||||||
|
transition: var(--animation-5s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-drag-drag-class {
|
||||||
|
opacity: 0;
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
190
src/components/sidebar/manage-group.js
Normal file
190
src/components/sidebar/manage-group.js
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
import { globalLookup } from "@/hook/global";
|
||||||
|
import { colorPicker, contextmenu, groupcontextmenu, handleGroupContextmenuFromSignalItem } from "./handle-contextmenu";
|
||||||
|
import { reactive, ref } from "vue";
|
||||||
|
import { findViewIndexByLink } from "@/hook/wave-container-view";
|
||||||
|
|
||||||
|
export const groupColors = [
|
||||||
|
{
|
||||||
|
color: '#dadce0',
|
||||||
|
used: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#7ca0d9',
|
||||||
|
used: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#e3847d',
|
||||||
|
used: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#fdd663',
|
||||||
|
used: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#7bbe8f',
|
||||||
|
used: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#ff8bcb',
|
||||||
|
used: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#c58af9',
|
||||||
|
used: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#73ccdf',
|
||||||
|
used: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#fcad70',
|
||||||
|
used: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const color2Index = {};
|
||||||
|
for (let i = 0; i < groupColors.length; ++ i) {
|
||||||
|
const color = groupColors[i].color;
|
||||||
|
color2Index[color] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 当前的组
|
||||||
|
* @type {WaveRenderSidebarItem[]}
|
||||||
|
*/
|
||||||
|
export const currentGroups = reactive([]);
|
||||||
|
|
||||||
|
export async function updateCurrentGroups() {
|
||||||
|
currentGroups.length = 0;
|
||||||
|
for (const view of globalLookup.currentWiresRenderView) {
|
||||||
|
if (view.renderType === 1) {
|
||||||
|
// 1 代表当前为 group
|
||||||
|
currentGroups.push(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const groupColorDispatcher = {
|
||||||
|
/**
|
||||||
|
* @description 获取颜色
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
get() {
|
||||||
|
let color = groupColors[0];
|
||||||
|
for (const c of groupColors) {
|
||||||
|
if (!c.used) {
|
||||||
|
c.used = true;
|
||||||
|
return c.color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description 归还颜色
|
||||||
|
* @param {string} color
|
||||||
|
*/
|
||||||
|
put(color) {
|
||||||
|
const index = color2Index[color];
|
||||||
|
if (index !== undefined && index < groupColors.length) {
|
||||||
|
groupColors[index].used = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export function makeColorStyle(color) {
|
||||||
|
return `background-color: ${color};`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个组,并且从 group 颜色调度器拿走一个颜色
|
||||||
|
export function createGroup() {
|
||||||
|
const signal = contextmenu.currentWire;
|
||||||
|
const wireIndex = contextmenu.currentWireIndex;
|
||||||
|
|
||||||
|
if (signal) {
|
||||||
|
const renderView = globalLookup.currentWiresRenderView;
|
||||||
|
if (wireIndex < renderView.length) {
|
||||||
|
const view = renderView[wireIndex];
|
||||||
|
view.renderType = 1;
|
||||||
|
const groupLink = view.signalInfo.link + '-group';
|
||||||
|
view.signalInfo.link = groupLink;
|
||||||
|
view.groupInfo = {
|
||||||
|
name: '',
|
||||||
|
color: groupColorDispatcher.get(),
|
||||||
|
collapse: false
|
||||||
|
};
|
||||||
|
view.children.push({
|
||||||
|
signalInfo: {
|
||||||
|
name: signal.name,
|
||||||
|
link: signal.link,
|
||||||
|
size: signal.size,
|
||||||
|
parentLink: groupLink
|
||||||
|
},
|
||||||
|
groupInfo: {
|
||||||
|
name: '',
|
||||||
|
color: '',
|
||||||
|
collapse: false
|
||||||
|
},
|
||||||
|
renderType: 0,
|
||||||
|
children: []
|
||||||
|
});
|
||||||
|
|
||||||
|
handleGroupContextmenuFromSignalItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contextmenu.show = false;
|
||||||
|
globalLookup.render();
|
||||||
|
updateCurrentGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cancelGroup() {
|
||||||
|
const currentGroupView = groupcontextmenu.currentGroupView;
|
||||||
|
if (currentGroupView) {
|
||||||
|
const link = currentGroupView.signalInfo.link;
|
||||||
|
const renderView = globalLookup.currentWiresRenderView;
|
||||||
|
|
||||||
|
const i = findViewIndexByLink(link);
|
||||||
|
if (i > 0) {
|
||||||
|
// 此时 renderView[i] 就是 currentGroupView,我们需要替换它
|
||||||
|
// 把 i 之后的元素保存起来
|
||||||
|
const tailElements = renderView.slice(i + 1);
|
||||||
|
renderView.length = i;
|
||||||
|
// 把 i 的 children 先加进来,然后再把保存的元素加进来
|
||||||
|
currentGroupView.children.forEach(view => renderView.push(view));
|
||||||
|
tailElements.forEach(view => renderView.push(view));
|
||||||
|
// 归换颜色
|
||||||
|
groupColorDispatcher.put(currentGroupView.groupInfo.color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
groupcontextmenu.show = false;
|
||||||
|
updateCurrentGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function deleteGroup() {
|
||||||
|
const currentGroupView = groupcontextmenu.currentGroupView;
|
||||||
|
if (currentGroupView) {
|
||||||
|
const link = currentGroupView.signalInfo.link;
|
||||||
|
const renderView = globalLookup.currentWiresRenderView;
|
||||||
|
|
||||||
|
const i = findViewIndexByLink(link);
|
||||||
|
if (i > 0) {
|
||||||
|
const tailElements = renderView.slice(i + 1);
|
||||||
|
renderView.length = i;
|
||||||
|
tailElements.forEach(view => renderView.push(view));
|
||||||
|
|
||||||
|
currentGroupView.children.forEach(view => {
|
||||||
|
const signal = globalLookup.link2CurrentWires.get(view.signalInfo.link);
|
||||||
|
if (signal) {
|
||||||
|
globalLookup.currentWires.delete(signal);
|
||||||
|
globalLookup.link2CurrentWires.delete(signal.link);
|
||||||
|
delete globalLookup.currentSignalValues[signal.link];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
groupColorDispatcher.put(currentGroupView.groupInfo.color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
groupcontextmenu.show = false;
|
||||||
|
updateCurrentGroups();
|
||||||
|
}
|
227
src/components/sidebar/signal-context-menu.vue
Normal file
227
src/components/sidebar/signal-context-menu.vue
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
<template>
|
||||||
|
<div class="sidebar-context-menu">
|
||||||
|
<div class="menu-container">
|
||||||
|
<div
|
||||||
|
class="menu-item-container"
|
||||||
|
@click="deleteSignalByView()"
|
||||||
|
>
|
||||||
|
<span class="menu-item-icon iconfont icon-delete"></span>
|
||||||
|
<span class="menu-item-name">{{ t('context-menu.delete') }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="menu-item-container"
|
||||||
|
@click="createGroup()"
|
||||||
|
>
|
||||||
|
<span class="menu-item-icon iconfont icon-group"></span>
|
||||||
|
<span class="menu-item-name">{{ t('context-menu.create-group') }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="menu-item-container"
|
||||||
|
@mouseenter="existGroup.show()"
|
||||||
|
@mouseleave="existGroup.hide()"
|
||||||
|
>
|
||||||
|
<span class="menu-item-icon iconfont icon-join-group"></span>
|
||||||
|
<span class="menu-item-name">{{ t('context-menu.join-group') }}</span>
|
||||||
|
<transition name="collapse-from-top">
|
||||||
|
<ExistGroup v-show="existGroup.display"></ExistGroup>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="menu-item-container"
|
||||||
|
@mouseenter="colorPicker.show()"
|
||||||
|
@mouseleave="colorPicker.hide()"
|
||||||
|
>
|
||||||
|
<span class="menu-item-icon iconfont icon-change-color"></span>
|
||||||
|
<span class="menu-item-name">{{ t('context-menu.change-color') }}</span>
|
||||||
|
<transition name="collapse-from-top">
|
||||||
|
<ColorPicker v-show="colorPicker.display"></ColorPicker>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div
|
||||||
|
v-if="contextmenu.currentWire"
|
||||||
|
class="basic-info"
|
||||||
|
>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-item-title">{{ t('context-menu.signal.name') }}</span>
|
||||||
|
<span>
|
||||||
|
{{ contextmenu.currentWire.name }} (<code>{{contextmenu.currentWire.link}}</code>)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-item-title">{{ t('context-menu.signal.type') }}</span>
|
||||||
|
<span>
|
||||||
|
<span :class="makeSignalIconClass(contextmenu.currentWire)"></span>
|
||||||
|
<span>{{ contextmenu.currentWire.type }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-item-title">{{ t('context-menu.signal.width') }}</span>
|
||||||
|
<span>{{ contextmenu.currentWire.size }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-item-title">{{ t('context-menu.signal.dep') }}</span>
|
||||||
|
<span v-html="makeFullSignalNameDeps(contextmenu.currentWire)"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineComponent, reactive, ref } from 'vue';
|
||||||
|
|
||||||
|
import { globalLookup } from '@/hook/global';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { colorPicker, contextmenu, deleteSignalByView } from './handle-contextmenu';
|
||||||
|
import { makeIconClass } from '@/hook/utils';
|
||||||
|
import ColorPicker from './color-picker.vue';
|
||||||
|
import ExistGroup from './exist-group.vue';
|
||||||
|
import { existGroup } from './exist-group';
|
||||||
|
import { createGroup } from './manage-group';
|
||||||
|
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
defineComponent({ name: 'signal-context-menu' });
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
|
||||||
|
});
|
||||||
|
const emits = defineEmits([]);
|
||||||
|
|
||||||
|
const predefineColors = reactive([
|
||||||
|
'#FF0000',
|
||||||
|
'#1AD834',
|
||||||
|
'#33E633',
|
||||||
|
'#FF4D7C',
|
||||||
|
'#ff4500',
|
||||||
|
'#ff8c00',
|
||||||
|
'#ffd700',
|
||||||
|
'#90ee90',
|
||||||
|
'#00ced1',
|
||||||
|
'#1e90ff',
|
||||||
|
'#c71585',
|
||||||
|
'#801fff',
|
||||||
|
'#7ca532',
|
||||||
|
]);
|
||||||
|
|
||||||
|
function makeSignalIconClass(signal) {
|
||||||
|
return 'iconfont ' + makeIconClass(signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {WireItemBaseInfo} signal
|
||||||
|
*/
|
||||||
|
function makeFullSignalNameDeps(signal) {
|
||||||
|
const deps = [];
|
||||||
|
while (signal) {
|
||||||
|
if (signal.name && signal.type) {
|
||||||
|
deps.push(signal);
|
||||||
|
}
|
||||||
|
signal = signal.parent;
|
||||||
|
}
|
||||||
|
let htmlString = '';
|
||||||
|
for (let i = deps.length - 1; i >= 0; -- i) {
|
||||||
|
const mod = deps[i];
|
||||||
|
// const displayName = mod.name.length > 6 ? mod.name.substring(0, 6) + '...' : mod.name;
|
||||||
|
const iconClass = makeIconClass(mod);
|
||||||
|
const iconText = `<span class="iconfont ${iconClass}"></span>${mod.name}`;
|
||||||
|
|
||||||
|
htmlString += iconText;
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
htmlString += '<div class="dep-arrow"></div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
htmlString = '<div class="signal-info-tooltip-wrapper">' + htmlString + '</div>';
|
||||||
|
return htmlString;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.sidebar-context-menu {
|
||||||
|
z-index: 200;
|
||||||
|
border-radius: .5em;
|
||||||
|
padding: 10px;
|
||||||
|
position: fixed;
|
||||||
|
box-shadow: 0 0 10px 1px rgb(16, 16, 16);
|
||||||
|
background-color: var(--sidebar);
|
||||||
|
border: solid 1px var(--sidebar-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-container {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 5px;
|
||||||
|
padding: 3px;
|
||||||
|
padding-left: 10px;
|
||||||
|
border-radius: .5em;
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
cursor: pointer;
|
||||||
|
width: 95%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-container:hover {
|
||||||
|
color: var(--sidebar);
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
#7CA532 25%,
|
||||||
|
rgba(200, 200, 200, 1) 37%,
|
||||||
|
rgba(255, 255, 255, 0) 63%
|
||||||
|
);
|
||||||
|
background-size: 400% 100%;
|
||||||
|
animation: loading-mask 1.5s cubic-bezier(0.23,1,0.32,1);
|
||||||
|
-webkit-animation: loading-mask 1.5s cubic-bezier(0.23,1,0.32,1);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-icon {
|
||||||
|
width: 42px;
|
||||||
|
font-size: 1.0rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.basic-info {
|
||||||
|
font-size: .8rem;
|
||||||
|
padding: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item-title {
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item code {
|
||||||
|
background-color: var(--button);
|
||||||
|
border-radius: .45em;
|
||||||
|
padding: 1px 5px;
|
||||||
|
margin: 1px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item .iconfont {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-container .el-color-picker__panel {
|
||||||
|
box-shadow: 0 0 10px 1px rgb(16, 16, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-color-picker__panel button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
107
src/components/sidebar/signal-item.vue
Normal file
107
src/components/sidebar/signal-item.vue
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<div class="signal-item"
|
||||||
|
:class="{
|
||||||
|
'active': globalLookup.sidebarSelectedWireLinks.has(props.view.signalInfo.link),
|
||||||
|
'right-active': contextmenu.isSameSignal(props.view) && contextmenu.show
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="display-signal-item">
|
||||||
|
<div class="signal-color-vendor">
|
||||||
|
<span :class="makeSignalIconClass(signalInfo)"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="signal-info">
|
||||||
|
<div class="signal-info">
|
||||||
|
<div class="signal-info-name">
|
||||||
|
<span class="signal-parent-info" v-show="showParent">{{ getParentName(signalInfo) }}</span>
|
||||||
|
<span>{{ signalInfo.name }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="signal-info-width" v-show="showWidth">
|
||||||
|
<div :class="signalInfo.size > 1 ? 'signal-info-caption' : ''">
|
||||||
|
{{ makeSignalCaption(signalInfo) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="signal-info-current-value-wrapper">
|
||||||
|
<span></span>
|
||||||
|
<el-tooltip
|
||||||
|
effect="dark"
|
||||||
|
:content="globalLookup.currentSignalValues[signalInfo.link] + ''"
|
||||||
|
placement="top"
|
||||||
|
raw-content
|
||||||
|
><div class="signal-info-current-value">
|
||||||
|
{{ globalLookup.currentSignalValues[signalInfo.link] }}
|
||||||
|
</div></el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { globalLookup, globalSetting } from '@/hook/global';
|
||||||
|
import { makeIconClass } from '@/hook/utils';
|
||||||
|
import { defineComponent, computed } from 'vue';
|
||||||
|
import { contextmenu } from './handle-contextmenu';
|
||||||
|
|
||||||
|
defineComponent({ name: 'signal-item' });
|
||||||
|
|
||||||
|
const showWidth = computed(() => globalSetting.displaySignalInfoScope.includes('width'));
|
||||||
|
const showParent = computed(() => globalSetting.displaySignalInfoScope.includes('parent'));
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
view: {
|
||||||
|
type: Object,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
signalInfo: {
|
||||||
|
type: Object,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const signalInfo = computed(() => {
|
||||||
|
const info = (props.view || {}).signalInfo || props.signalInfo;
|
||||||
|
return info;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {WireItemBaseInfo} signal
|
||||||
|
*/
|
||||||
|
function makeSignalIconClass(signal) {
|
||||||
|
const realSignal = globalLookup.link2CurrentWires.get(signal.link);
|
||||||
|
return 'iconfont ' + makeIconClass(realSignal);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSignalCaption(signal) {
|
||||||
|
if (signal === undefined) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return signal.size === 1 ? '' : `${signal.size - 1}:0`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {WireItemBaseInfo} signal
|
||||||
|
*/
|
||||||
|
function getParentName(signal) {
|
||||||
|
if (signal === undefined) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const realSignal = globalLookup.link2CurrentWires.get(signal.link);
|
||||||
|
if (realSignal) {
|
||||||
|
return realSignal.parent.name;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.signal-item {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
41
src/components/toolbar/current-status.vue
Normal file
41
src/components/toolbar/current-status.vue
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 当前选择的波形的颜色,选择的数量等等 -->
|
||||||
|
<div class="status">
|
||||||
|
+ {{ selectedSignalNumber }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { globalLookup } from '@/hook/global';
|
||||||
|
import { computed, defineComponent } from 'vue';
|
||||||
|
|
||||||
|
defineComponent({ name: 'current-status' });
|
||||||
|
|
||||||
|
const selectedSignalNumber = computed(() => {
|
||||||
|
return globalLookup.sidebarSelectedWireLinks.size;
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedColors = computed(() => {
|
||||||
|
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.status {
|
||||||
|
color: var(--sidebar);
|
||||||
|
border-radius: .5em;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
background-color: var(--main-color);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
65
src/components/toolbar/cursor-location.vue
Normal file
65
src/components/toolbar/cursor-location.vue
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<template>
|
||||||
|
<div class="location">
|
||||||
|
<div
|
||||||
|
class="item iconfont icon-beginning"
|
||||||
|
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="item iconfont icon-ending"
|
||||||
|
|
||||||
|
/>
|
||||||
|
<el-divider direction="vertical"></el-divider>
|
||||||
|
<div
|
||||||
|
class="item iconfont icon-arrow-to-previous"
|
||||||
|
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="item iconfont icon-arrow-to-next"
|
||||||
|
|
||||||
|
/>
|
||||||
|
<el-divider direction="vertical"></el-divider>
|
||||||
|
<div
|
||||||
|
class="item iconfont icon-add-line"
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
defineComponent({ name: 'cursor-location' });
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.location {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 32px;
|
||||||
|
padding: 1px 5px;
|
||||||
|
background-color: var(--sidebar);
|
||||||
|
border-radius: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.location .item {
|
||||||
|
border-radius: .5em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-left: 3px;
|
||||||
|
height: 32px;
|
||||||
|
font-weight: 800;
|
||||||
|
width: 32px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.location .item:hover {
|
||||||
|
background-color: var(--main-color);
|
||||||
|
color: var(--sidebar);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,29 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="toolbar-container">
|
<div class="toolbar-container">
|
||||||
<div class="toolbar-body">
|
<div class="toolbar-body">
|
||||||
<div>
|
<CurrentStatus></CurrentStatus>
|
||||||
|
 
|
||||||
</div>
|
<SignalModal></SignalModal>
|
||||||
|
 
|
||||||
|
<SignalValueFormat></SignalValueFormat>
|
||||||
|
 
|
||||||
|
<CursorLocation></CursorLocation>
|
||||||
|
 
|
||||||
|
<ValueSearch></ValueSearch>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import CurrentStatus from './current-status.vue';
|
||||||
|
import SignalModal from './signal-modal.vue';
|
||||||
|
import SignalValueFormat from './signal-value-format.vue';
|
||||||
|
import CursorLocation from './cursor-location.vue';
|
||||||
|
import ValueSearch from './value-search.vue';
|
||||||
|
|
||||||
|
|
||||||
|
defineComponent({ name: 'toolbar' });
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.toolbar-container {
|
.toolbar-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 10px;
|
top: 0;
|
||||||
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
height: var(--toolbar-height);
|
||||||
justify-content: center;
|
background-color: var(--background);
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-body {
|
.toolbar-body {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
align-items: top;
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-top: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
3
src/components/toolbar/signal-modal.js
Normal file
3
src/components/toolbar/signal-modal.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { reactive } from "vue";
|
||||||
|
|
||||||
|
export const signalModal = reactive();
|
59
src/components/toolbar/signal-modal.vue
Normal file
59
src/components/toolbar/signal-modal.vue
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<div class="">
|
||||||
|
<el-radio-group v-model="signalModal">
|
||||||
|
<!-- 数字模式 -->
|
||||||
|
<el-radio-button :label="0">
|
||||||
|
<el-tooltip
|
||||||
|
effect="dark"
|
||||||
|
:content="t('toolbar.modal.common-digital')"
|
||||||
|
placement="bottom"
|
||||||
|
raw-content
|
||||||
|
>
|
||||||
|
<span class="iconfont icon-common-digital"></span>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-radio-button>
|
||||||
|
<!-- 折现模拟 -->
|
||||||
|
<el-radio-button :label="1">
|
||||||
|
<el-tooltip
|
||||||
|
effect="dark"
|
||||||
|
:content="t('toolbar.modal.ladder-analog')"
|
||||||
|
placement="bottom"
|
||||||
|
raw-content
|
||||||
|
><span class="iconfont icon-ladder-analog"></span></el-tooltip>
|
||||||
|
</el-radio-button>
|
||||||
|
<!-- 阶梯模拟 -->
|
||||||
|
<el-radio-button :label="2">
|
||||||
|
<el-tooltip
|
||||||
|
effect="dark"
|
||||||
|
:content="t('toolbar.modal.line-analog')"
|
||||||
|
placement="bottom"
|
||||||
|
raw-content
|
||||||
|
><span class="iconfont icon-line-analog"></span></el-tooltip>
|
||||||
|
|
||||||
|
</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineComponent, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
// 负责展示波形形式的模块,分为数字形式、折线模拟形式、阶梯模拟形式
|
||||||
|
defineComponent({ name: 'signal-modal' });
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const signalModal = ref(0);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.iconfont {
|
||||||
|
font-size: 1.10em;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
90
src/components/toolbar/signal-value-format.vue
Normal file
90
src/components/toolbar/signal-value-format.vue
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<template>
|
||||||
|
<div class="signal-format">
|
||||||
|
<el-select
|
||||||
|
v-model="currentOption"
|
||||||
|
>
|
||||||
|
<el-option-group
|
||||||
|
v-for="group in formatOptions"
|
||||||
|
:key="group.label"
|
||||||
|
:label="group.label"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in group.options"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
>
|
||||||
|
</el-option>
|
||||||
|
</el-option-group>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineComponent, reactive, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
// 负责展示波形 数值的 类型,分为 二进制、十六进制、八进制、十进制、浮点数(半精度、单精度、双精度)
|
||||||
|
defineComponent({ name: 'signal-value-format' });
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const currentOption = ref(0);
|
||||||
|
const formatOptions = reactive([
|
||||||
|
{
|
||||||
|
label: t('toolbar.format.category.base'),
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
label: 'Bin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 1,
|
||||||
|
label: 'Oct'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 2,
|
||||||
|
label: 'Hex'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('toolbar.format.category.dec'),
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: 3,
|
||||||
|
label: t('toolbar.format.signed')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 4,
|
||||||
|
label: t('toolbar.format.unsigned')
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('toolbar.format.category.float'),
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: 5,
|
||||||
|
label: t('toolbar.format.half')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 6,
|
||||||
|
label: t('toolbar.format.float')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 7,
|
||||||
|
label: t('toolbar.format.double')
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.signal-format {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
</style>
|
48
src/components/toolbar/value-search.vue
Normal file
48
src/components/toolbar/value-search.vue
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<div class="value-search">
|
||||||
|
<div>
|
||||||
|
<el-radio-group
|
||||||
|
v-model="searchMode"
|
||||||
|
style="border-radius: 1.2em;"
|
||||||
|
>
|
||||||
|
<el-radio-button :label="0">
|
||||||
|
<span>{{ t('toolbar.search.value') }}</span>
|
||||||
|
</el-radio-button>
|
||||||
|
<el-radio-button :label="1">
|
||||||
|
<span>{{ t('toolbar.search.name') }}</span>
|
||||||
|
</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
</div>
|
||||||
|
<div class="value-input-wrapper">
|
||||||
|
<el-input></el-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineComponent, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
defineComponent({ name: 'value-search' })
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const searchMode = ref(0);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.value-search {
|
||||||
|
margin-left: 5px;
|
||||||
|
padding-top: 2px;
|
||||||
|
display: flex;
|
||||||
|
height: 32px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-input-wrapper {
|
||||||
|
margin-left: 5px;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
@ -2,18 +2,16 @@
|
|||||||
<!-- treeview -->
|
<!-- treeview -->
|
||||||
<div class="vcd-treeview">
|
<div class="vcd-treeview">
|
||||||
<TreeViewSearch></TreeViewSearch>
|
<TreeViewSearch></TreeViewSearch>
|
||||||
<br>
|
|
||||||
<div class="vcd-module-wrapper">
|
<div class="vcd-module-wrapper">
|
||||||
<div class="vcd-module-info">
|
<div class="vcd-module-info">
|
||||||
<div class="vcd-signal-title">{{ t('module') }}</div>
|
<div class="vcd-signal-title">{{ t('module') }}</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="vcd-module-display-wrapper">
|
<el-scrollbar height="86vh" style="padding-right: 7px;">
|
||||||
<Modules v-for="mod of props.topModules"
|
<Modules v-for="mod of props.topModules"
|
||||||
:key="mod.name"
|
:key="mod.name"
|
||||||
:module="mod"
|
:module="mod"
|
||||||
></Modules>
|
></Modules>
|
||||||
</div>
|
</el-scrollbar>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="vcd-module-wires">
|
<div class="vcd-module-wires">
|
||||||
<Signals></Signals>
|
<Signals></Signals>
|
||||||
@ -63,10 +61,6 @@ export default {
|
|||||||
width: 220px;
|
width: 220px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-module-display-wrapper {
|
|
||||||
height: 90vh;
|
|
||||||
overflow: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vcd-treeview {
|
.vcd-treeview {
|
||||||
background-color: var(--sidebar);
|
background-color: var(--sidebar);
|
||||||
|
@ -2,24 +2,26 @@
|
|||||||
<div
|
<div
|
||||||
class="tree-view-search-wrapper"
|
class="tree-view-search-wrapper"
|
||||||
>
|
>
|
||||||
<div>
|
<div style="height: 5vh;">
|
||||||
<el-input
|
<el-input
|
||||||
:placeholder="t('search-signal')"
|
:placeholder="t('search-signal')"
|
||||||
size="large"
|
|
||||||
v-model="searchManage.content"
|
v-model="searchManage.content"
|
||||||
input-style="font-size: 16px;"
|
|
||||||
@input="safeSearch"
|
@input="safeSearch"
|
||||||
@focus="searchManage.displayResult = true"
|
@focus="searchManage.displayResult = true"
|
||||||
@blur="searchManage.displayResult = false"
|
@blur="searchManage.displayResult = false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<transition name="collapse-from-top">
|
||||||
<div
|
<div
|
||||||
v-if="searchManage.displayResult | searchManage.mouseOnResult"
|
v-show="searchManage.displayResult | searchManage.mouseOnResult"
|
||||||
:style="searchResultWrapper"
|
:style="searchResultWrapper"
|
||||||
class="search-result-wrapper"
|
class="search-result-wrapper"
|
||||||
id="search-result-wrapper"
|
id="search-result-wrapper"
|
||||||
>
|
>
|
||||||
<div class="search-result"
|
<el-scrollbar
|
||||||
|
height="50vh"
|
||||||
|
width="600px"
|
||||||
|
class="search-result"
|
||||||
@mouseenter="searchManage.mouseOnResult = true"
|
@mouseenter="searchManage.mouseOnResult = true"
|
||||||
@mouseleave="searchManage.mouseOnResult = false"
|
@mouseleave="searchManage.mouseOnResult = false"
|
||||||
>
|
>
|
||||||
@ -31,13 +33,15 @@
|
|||||||
<div v-html="searchResult.htmlString" class="search-result-item"></div>
|
<div v-html="searchResult.htmlString" class="search-result-item"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else class="search-nothing">
|
||||||
{{ t('search-nothing') }}
|
<span class="iconfont icon-empty"></span>
|
||||||
|
<span>{{ t('search-nothing') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
|
</transition>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -70,6 +74,7 @@ function search() {
|
|||||||
const searchString = searchManage.content.trim();
|
const searchString = searchManage.content.trim();
|
||||||
if (searchString.length === 0) {
|
if (searchString.length === 0) {
|
||||||
searchManage.displayResult = false;
|
searchManage.displayResult = false;
|
||||||
|
searchManage.searchResult = []
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +116,6 @@ const safeSearch = debounceWrapper(search, 500);
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.tree-view-search-wrapper {
|
.tree-view-search-wrapper {
|
||||||
min-height: 30px;
|
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
transition: flex .5s ease-in-out;
|
transition: flex .5s ease-in-out;
|
||||||
}
|
}
|
||||||
@ -129,11 +133,8 @@ const safeSearch = debounceWrapper(search, 500);
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search-result {
|
.search-result {
|
||||||
overflow-x: scroll;
|
|
||||||
overflow-y: scroll;
|
|
||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
|
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,8 +158,30 @@ const safeSearch = debounceWrapper(search, 500);
|
|||||||
width: 7px;
|
width: 7px;
|
||||||
border-top: solid 1.7px var(--sidebar-item-text);
|
border-top: solid 1.7px var(--sidebar-item-text);
|
||||||
border-left: solid 1.7px var(--sidebar-item-text);
|
border-left: solid 1.7px var(--sidebar-item-text);
|
||||||
transform: rotate(135deg);
|
transform: rotate(225deg);
|
||||||
margin-right: 8px;
|
margin-top: 6px;
|
||||||
|
margin-bottom: 8px;
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-result-item .dep-arrow {
|
||||||
|
transform: rotate(135deg);
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-nothing {
|
||||||
|
height: 40vh;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-nothing .iconfont {
|
||||||
|
font-size: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
@ -2,11 +2,12 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="vcd-signal-title">{{ t('signal') }}({{signals.content.length}})</div>
|
<div class="vcd-signal-title">{{ t('signal') }}({{signals.content.length}})</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="vcd-signal-signals-display">
|
<el-scrollbar height="86vh" class="vcd-signal-signals-display">
|
||||||
<div v-for="(signal, index) in signals.content" :key="index"
|
<div v-for="(signal, index) in signals.content" :key="index"
|
||||||
@click="toggleRender(signal)"
|
@click="toggleRender(signal)"
|
||||||
class="vcd-signal-signal-item"
|
class="vcd-signal-signal-item"
|
||||||
:class="globalLookup.currentWires.has(signal) ? 'vcd-treeview-selected' : ''">
|
:class="globalLookup.currentWires.has(signal) ? 'vcd-treeview-selected' : ''"
|
||||||
|
>
|
||||||
<div class="vcd-signal-signal-item-text"><span :class="`iconfont ${makeIconClass(signal)}`"></span> {{ signal.name }}</div>
|
<div class="vcd-signal-signal-item-text"><span :class="`iconfont ${makeIconClass(signal)}`"></span> {{ signal.name }}</div>
|
||||||
<div>
|
<div>
|
||||||
<div :class="signal.size > 1 ? 'vcd-signal-signal-caption' : ''">
|
<div :class="signal.size > 1 ? 'vcd-signal-signal-caption' : ''">
|
||||||
@ -14,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -62,6 +63,7 @@ export default {
|
|||||||
<style>
|
<style>
|
||||||
.vcd-signal-title {
|
.vcd-signal-title {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
height: 2.5vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-wave-square {
|
.icon-wave-square {
|
||||||
@ -95,10 +97,9 @@ export default {
|
|||||||
.vcd-signal-signals-display {
|
.vcd-signal-signals-display {
|
||||||
color: var(--sidebar-item-text);
|
color: var(--sidebar-item-text);
|
||||||
padding: 0px 8px;
|
padding: 0px 8px;
|
||||||
height: 90vh;
|
|
||||||
overflow-x: scroll;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.vcd-signal-signal-item {
|
.vcd-signal-signal-item {
|
||||||
margin: 3px;
|
margin: 3px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -10,17 +10,30 @@ export const emitter = mitt();
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const globalLookup = reactive({
|
export const globalLookup = reactive({
|
||||||
// 所有的顶层文件
|
/**
|
||||||
|
* @description 所有的顶层文件
|
||||||
|
* @type {TopModWireItem[]}
|
||||||
|
*/
|
||||||
topModules: [],
|
topModules: [],
|
||||||
// 当前选中的 信号,也就是 tree-view 左列的,默认是第一个。不可复选。
|
|
||||||
|
/**
|
||||||
|
* @description 当前选中的 信号,也就是 tree-view 左列的,默认是第一个。不可复选。
|
||||||
|
* @type {WireItem | undefined}
|
||||||
|
*/
|
||||||
currentModule: undefined,
|
currentModule: undefined,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 当前被选中的波形,用于进行高效的增删改查
|
* @description 当前被选中的波形,用于进行高效的增删改查
|
||||||
* @type {Set<WaveRenderItem>}
|
* @type {Set<WireItem>}
|
||||||
*/
|
*/
|
||||||
currentWires: new Set(),
|
currentWires: new Set(),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 当前被选中的波形,是 currentWires 的字典版本
|
||||||
|
* @type {Map<string, WireItem>}
|
||||||
|
*/
|
||||||
|
link2CurrentWires: new Map(),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 当前被选中的波形,用于 sidebar 的渲染视图
|
* @description 当前被选中的波形,用于 sidebar 的渲染视图
|
||||||
* 详细请见设计文档:https://nc-ai-lab.feishu.cn/wiki/Fy3ZwtbYbiatmxkhOp2cyHSFnlw
|
* 详细请见设计文档:https://nc-ai-lab.feishu.cn/wiki/Fy3ZwtbYbiatmxkhOp2cyHSFnlw
|
||||||
@ -34,6 +47,19 @@ export const globalLookup = reactive({
|
|||||||
*/
|
*/
|
||||||
currentSignalValues: {},
|
currentSignalValues: {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 侧边栏中当前被选中的波形,一定是 currentWires 的子集
|
||||||
|
* 内部存储的是 wire 的 link,所以是 string
|
||||||
|
* @type {Set<string>}
|
||||||
|
*/
|
||||||
|
sidebarSelectedWireLinks: new Set(),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 当前渲染的信号的基本选项,比如高度,颜色,format,modal 等等
|
||||||
|
* @type {Map<string, >}
|
||||||
|
*/
|
||||||
|
currentSignalRenderOptions: new Map(),
|
||||||
|
|
||||||
// 当前 ns 数(或者 ps)
|
// 当前 ns 数(或者 ps)
|
||||||
currentTime: 0,
|
currentTime: 0,
|
||||||
|
|
||||||
@ -62,7 +88,14 @@ export const globalLookup = reactive({
|
|||||||
|
|
||||||
// 初始化时会被定义
|
// 初始化时会被定义
|
||||||
render: () => {},
|
render: () => {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 非常核心的用于操控 webgl 进行渲染和动画的核心类
|
||||||
|
* @type {WebGL2WaveRender}
|
||||||
|
*/
|
||||||
waveRender: undefined,
|
waveRender: undefined,
|
||||||
|
|
||||||
|
|
||||||
pstate: undefined,
|
pstate: undefined,
|
||||||
|
|
||||||
xScale: 1,
|
xScale: 1,
|
||||||
@ -88,9 +121,16 @@ export const globalLookup = reactive({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const globalStyle = reactive({
|
export const globalStyle = reactive({
|
||||||
timeScaleHeight: 30,
|
/**
|
||||||
|
* @description 这个值代表 sidebar 的上侧的空间,但是 sidebar 本身有一个 10px 的 padding,所以真实的 sidebar 上侧
|
||||||
|
* 空出来的空间等于 timeScaleHeight + 10,需要注意, timeScaleHeight 需要等于 pstate.yOffset 的初始值的相反数,
|
||||||
|
* pstate.yOffset 的初始值请参考 dom-container.js 这个文件
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
timeScaleHeight: 20,
|
||||||
|
|
||||||
sideBarPadding: 10,
|
sideBarPadding: 10,
|
||||||
sideBarItemMargin: 5,
|
sideBarItemMargin: 2,
|
||||||
vcdRenderPadding: 24,
|
vcdRenderPadding: 24,
|
||||||
yOffset: 0,
|
yOffset: 0,
|
||||||
});
|
});
|
||||||
|
@ -25,6 +25,9 @@ function debounceWrapper(fn, delay) {
|
|||||||
|
|
||||||
|
|
||||||
function makeIconClass(mod) {
|
function makeIconClass(mod) {
|
||||||
|
if (mod === undefined) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
switch (mod.type) {
|
switch (mod.type) {
|
||||||
case 'module': return 'icon-memory-chip';
|
case 'module': return 'icon-memory-chip';
|
||||||
case 'begin': return 'icon-brackets';
|
case 'begin': return 'icon-brackets';
|
||||||
|
@ -19,10 +19,22 @@ export const WaveContainerView = {
|
|||||||
// 增加高效 CRUD 视图
|
// 增加高效 CRUD 视图
|
||||||
globalLookup.currentWires.add(signal);
|
globalLookup.currentWires.add(signal);
|
||||||
|
|
||||||
|
// 上面的 视图 的 string, signal 字典版本
|
||||||
|
globalLookup.link2CurrentWires.set(signal.link, signal);
|
||||||
|
|
||||||
// 增加 sidebar 渲染视图
|
// 增加 sidebar 渲染视图
|
||||||
// TODO : 支持更加复杂的视图加入
|
// TODO : 支持更加复杂的视图加入
|
||||||
globalLookup.currentWiresRenderView.push({
|
globalLookup.currentWiresRenderView.push({
|
||||||
signal,
|
signalInfo: {
|
||||||
|
name: signal.name,
|
||||||
|
link: signal.link,
|
||||||
|
size: signal.size,
|
||||||
|
},
|
||||||
|
groupInfo: {
|
||||||
|
name: '',
|
||||||
|
color: '',
|
||||||
|
collapse: false
|
||||||
|
},
|
||||||
renderType: 0,
|
renderType: 0,
|
||||||
children: []
|
children: []
|
||||||
});
|
});
|
||||||
@ -38,8 +50,18 @@ export const WaveContainerView = {
|
|||||||
* @param {WireItem} signal
|
* @param {WireItem} signal
|
||||||
*/
|
*/
|
||||||
delete(signal) {
|
delete(signal) {
|
||||||
|
const renderView = globalLookup.currentWiresRenderView;
|
||||||
|
|
||||||
globalLookup.currentWires.delete(signal);
|
globalLookup.currentWires.delete(signal);
|
||||||
|
globalLookup.link2CurrentWires.delete(signal.link);
|
||||||
delete globalLookup.currentSignalValues[signal.link];
|
delete globalLookup.currentSignalValues[signal.link];
|
||||||
|
|
||||||
|
// 从 currentWiresRenderView 中删除
|
||||||
|
const i = findViewIndexByLink(signal.link);
|
||||||
|
const tailElements = renderView.slice(i + 1);
|
||||||
|
renderView.length = i;
|
||||||
|
tailElements.forEach(view => renderView.push(view));
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,3 +73,45 @@ export const WaveContainerView = {
|
|||||||
return globalLookup.currentWires.has(signal);
|
return globalLookup.currentWires.has(signal);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function findViewIndexByLink(link) {
|
||||||
|
const renderView = globalLookup.currentWiresRenderView;
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
while (renderView[i].signalInfo.link !== link && i < renderView.length) {
|
||||||
|
++ i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < renderView.length) {
|
||||||
|
return i;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function makeFullSignalNameDeps(signal) {
|
||||||
|
const deps = [];
|
||||||
|
while (signal) {
|
||||||
|
if (signal.name && signal.type) {
|
||||||
|
deps.push(signal);
|
||||||
|
}
|
||||||
|
signal = signal.parent;
|
||||||
|
}
|
||||||
|
let htmlString = '';
|
||||||
|
for (let i = deps.length - 1; i >= 0; -- i) {
|
||||||
|
const mod = deps[i];
|
||||||
|
// const displayName = mod.name.length > 6 ? mod.name.substring(0, 6) + '...' : mod.name;
|
||||||
|
const iconClass = makeIconClass(mod);
|
||||||
|
const iconText = `<span class="iconfont ${iconClass}"></span> ${mod.name}`;
|
||||||
|
|
||||||
|
htmlString += iconText;
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
htmlString += '<div class="dep-arrow"></div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
htmlString = '<div class="signal-info-tooltip-wrapper">' + htmlString + '</div>';
|
||||||
|
return htmlString;
|
||||||
|
}
|
@ -117,13 +117,13 @@ const domContainer = (obj) => {
|
|||||||
topBarHeight: fontHeight * 1.5, // [px]
|
topBarHeight: fontHeight * 1.5, // [px]
|
||||||
botBarHeight: fontHeight * 1.5, // [px]
|
botBarHeight: fontHeight * 1.5, // [px]
|
||||||
xOffset: sidebarWidth,
|
xOffset: sidebarWidth,
|
||||||
yOffset: -40, // [px]
|
yOffset: -20, // [px]
|
||||||
yStep: 54, // = 24 // [px] wave lane height
|
yStep: 54, // = 24 // [px] wave lane height
|
||||||
yDuty: 0.6,
|
yDuty: 0.6,
|
||||||
|
|
||||||
// for animation
|
// for animation
|
||||||
oldXOffset: sidebarWidth,
|
oldXOffset: sidebarWidth,
|
||||||
oldYOffset: -40,
|
oldYOffset: -20,
|
||||||
oldYStep: 54,
|
oldYStep: 54,
|
||||||
oldYDuty: 0.6,
|
oldYDuty: 0.6,
|
||||||
|
|
||||||
@ -155,6 +155,8 @@ const domContainer = (obj) => {
|
|||||||
time: deso.time
|
time: deso.time
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// TODO : 增加已有波形的保存和读取
|
||||||
try {
|
try {
|
||||||
const str = localStorage.getItem('dide');
|
const str = localStorage.getItem('dide');
|
||||||
const obj = JSON.parse(str);
|
const obj = JSON.parse(str);
|
||||||
|
@ -4,7 +4,11 @@ const genResizeHandler = pstate =>
|
|||||||
(width, height) => {
|
(width, height) => {
|
||||||
let { xOffset, yOffset, xScale, yStep, time, sidebarWidth, numLanes } = pstate;
|
let { xOffset, yOffset, xScale, yStep, time, sidebarWidth, numLanes } = pstate;
|
||||||
pstate.width = width;
|
pstate.width = width;
|
||||||
pstate.height = height;
|
|
||||||
|
// TODO: 此处用于管理 波形渲染区域 的整体方位
|
||||||
|
// 真实高度还要减去 上方 toolbar 的高度
|
||||||
|
// 此处的 60 为 --toolbar-height
|
||||||
|
pstate.height = height - 55;
|
||||||
|
|
||||||
// Y
|
// Y
|
||||||
const yOffsetMax = (numLanes + 2) * 2 * yStep;
|
const yOffsetMax = (numLanes + 2) * 2 * yStep;
|
||||||
|
@ -23,7 +23,11 @@ const getLabel = (lane) => {
|
|||||||
|
|
||||||
value = BigInt(value);
|
value = BigInt(value);
|
||||||
|
|
||||||
const txtShort = formatter(value, pos, width);
|
let txtShort = formatter(value, pos, width);
|
||||||
|
// TODO: 重构这里
|
||||||
|
if (txtShort.startsWith('...')) {
|
||||||
|
txtShort = txtShort.replace('...', '');
|
||||||
|
}
|
||||||
|
|
||||||
return ['text', { x, class: 'common' }, txtShort];
|
return ['text', { x, class: 'common' }, txtShort];
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { gl_Colors_template } from "./render-utils";
|
||||||
|
|
||||||
class ShaderMaker {
|
class ShaderMaker {
|
||||||
/**
|
/**
|
||||||
@ -26,12 +27,14 @@ class ShaderMaker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const colorsLength = gl_Colors_template.length << 1;
|
||||||
|
|
||||||
const vertexShader = new ShaderMaker('VERTEX_SHADER', `#version 300 es
|
const vertexShader = new ShaderMaker('VERTEX_SHADER', `#version 300 es
|
||||||
in uvec4 pos;
|
in uvec4 pos;
|
||||||
out vec4 v_color;
|
out vec4 v_color;
|
||||||
uniform vec2 scale;
|
uniform vec2 scale;
|
||||||
uniform vec2 offset;
|
uniform vec2 offset;
|
||||||
uniform vec4 colors[16];
|
uniform vec4 colors[${colorsLength}];
|
||||||
uniform vec2 shifts[7]; // 基础八位图偏移量,为了性能,pos 只传入整数,需要的坐标负数由该值提供
|
uniform vec2 shifts[7]; // 基础八位图偏移量,为了性能,pos 只传入整数,需要的坐标负数由该值提供
|
||||||
uniform vec2 widthShifts[8]; // 用于构造线宽的偏移
|
uniform vec2 widthShifts[8]; // 用于构造线宽的偏移
|
||||||
|
|
||||||
|
@ -24,29 +24,56 @@ function getRatio() {
|
|||||||
const screenHeightPixel = window.screen.height * getRatio() / 100;
|
const screenHeightPixel = window.screen.height * getRatio() / 100;
|
||||||
const screenWidthPixel = window.screen.width * getRatio() / 100;
|
const screenWidthPixel = window.screen.width * getRatio() / 100;
|
||||||
|
|
||||||
// 控制颜色
|
// rgba 颜色通道,都是预设的颜色
|
||||||
const gl_Colors = new Float32Array([
|
export const gl_Colors_template = [
|
||||||
0, 0, 0, 0, // 0: 空
|
[0, 0, 0, 0 ], // 0: 空
|
||||||
|
[0, 0, 255, 1], // 1: 未知态 X 默认颜色
|
||||||
|
[51, 230, 26, 1], // 2: value = 0 用于 width = 1 的信号 默认颜色
|
||||||
|
[51, 230, 26, 1], // 3: value = 1 用于 width = 1 的信号 默认颜色
|
||||||
|
[230, 51, 51, 1], // 4: 高阻态 Z 默认颜色
|
||||||
|
[124, 77, 255, 1], // 5: vec 用于 width > 1 的信号
|
||||||
|
[255, 0, 255, 1], // 6: yellow
|
||||||
|
[255, 0, 255, 1], // 7: strange purple
|
||||||
|
[0, 255, 0, 0.5], // 8: (l L) weak 0
|
||||||
|
[255, 0, 255, 0.5], // 9: (h H) weak 1
|
||||||
|
[255, 0, 0, 0.5], // 10: (w W) weak unknown
|
||||||
|
|
||||||
0, 0, 1, 1, // 1: 未知态 X
|
// >= 11 自定义颜色
|
||||||
0.2, 0.847, 0.1, 1, // 2: value = 0 用于 width = 1 的信号
|
[255, 140, 0, 1],
|
||||||
0.2, 0.847, 0.1, 1, // 3: value = 1 用于 width = 1 的信号
|
[255, 140, 0, 1],
|
||||||
0.9, 0.2, 0.2, 1, // 4: 高阻态 Z
|
[144, 238, 144, 1],
|
||||||
0.486, 0.302, 1., 1, // 5: vec 用于 width > 1 的信号
|
[0, 206, 209, 1],
|
||||||
|
[30, 144, 255, 1],
|
||||||
|
[199, 21, 133, 1],
|
||||||
|
[128, 31, 255, 1],
|
||||||
|
[124, 165, 50, 1]
|
||||||
|
];
|
||||||
|
|
||||||
1, 1, 0, 1, // 6: yellow
|
function makeGlColors() {
|
||||||
1, 0, 1, 1, // 7: strange purple
|
const glcolors = [];
|
||||||
|
for (const rgba of gl_Colors_template) {
|
||||||
|
const r = rgba[0] / 255;
|
||||||
|
const g = rgba[1] / 255;
|
||||||
|
const b = rgba[2] / 255;
|
||||||
|
const a = rgba[3];
|
||||||
|
glcolors.push(r, g, b, a);
|
||||||
|
}
|
||||||
|
// 制作 mask
|
||||||
|
for (const rgba of gl_Colors_template) {
|
||||||
|
const r = rgba[0] / 255;
|
||||||
|
const g = rgba[1] / 255;
|
||||||
|
const b = rgba[2] / 255;
|
||||||
|
glcolors.push(r, g, b, 0.1);
|
||||||
|
}
|
||||||
|
return glcolors;
|
||||||
|
}
|
||||||
|
|
||||||
0, 1, 0, .5, // 8: (l L) weak 0
|
|
||||||
0, 1, 1, .5, // 9: (h H) weak 1
|
|
||||||
1, 0, 0, .5, // 10: (w W) weak unknown
|
|
||||||
|
|
||||||
0, 0, 1, 0.1, // 11: 未知态 X 遮罩
|
// 根据 gl_Colors_template 计算出 gl_Colors
|
||||||
0.2, 0.847, 0.1, 0.1, // 12: value = 0 遮罩
|
// 控制颜色 0 - 1 归一化的 bgr
|
||||||
0.2, 0.847, 0.1, 0.1, // 13: value = 1 遮罩
|
const gl_Colors = new Float32Array(makeGlColors());
|
||||||
0.9, 0.2, 0.2, 0.1, // 14: 高阻态 Z 遮罩
|
|
||||||
0.486, 0.302, 1., 0.1 // 15: vec 遮罩
|
export let maskColorIndexOffset = gl_Colors_template.length;
|
||||||
]);
|
|
||||||
|
|
||||||
|
|
||||||
// 特殊偏移量
|
// 特殊偏移量
|
||||||
|
@ -8,6 +8,7 @@ import vline from './vline';
|
|||||||
import getX from './get-x';
|
import getX from './get-x';
|
||||||
import vlineStylo from './vline-stylo';
|
import vlineStylo from './vline-stylo';
|
||||||
import getLabel from './get-label';
|
import getLabel from './get-label';
|
||||||
|
import { globalLookup } from '../global.js';
|
||||||
|
|
||||||
const defs = ['defs',
|
const defs = ['defs',
|
||||||
['linearGradient', { id: 'valid' },
|
['linearGradient', { id: 'valid' },
|
||||||
@ -32,18 +33,56 @@ const defs = ['defs',
|
|||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {GlobalLookup} desc
|
||||||
|
* @param {*} pstate
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
function* renderValues(desc, pstate) {
|
function* renderValues(desc, pstate) {
|
||||||
const { width, height, sidebarWidth, yOffset, yStep, topBarHeight, botBarHeight } = pstate;
|
const { width, height, yOffset, yStep, topBarHeight, botBarHeight } = pstate;
|
||||||
|
|
||||||
|
// TODO: 对齐参数
|
||||||
|
const sidebarWidth = 230;
|
||||||
|
// 根据 currentWiresRenderView 视图渲染
|
||||||
|
// 此处应该和 render-wave 的相同注释的地方保持逻辑上的一致(render-wave.js 约 646 行)
|
||||||
|
|
||||||
const currentWires = desc.currentWires;
|
const currentWires = desc.currentWires;
|
||||||
const view = [];
|
const renderSignals = [];
|
||||||
for (const signal of currentWires) {
|
const link2CurrentWires = globalLookup.link2CurrentWires;
|
||||||
view.push({
|
for (const view of globalLookup.currentWiresRenderView) {
|
||||||
kind: signal.kind,
|
if (view.renderType === 0) {
|
||||||
name: signal.name,
|
const realSignal = link2CurrentWires.get(view.signalInfo.link);
|
||||||
ref: signal.link
|
renderSignals.push({
|
||||||
|
name: realSignal.name,
|
||||||
|
kind: realSignal.kind,
|
||||||
|
ref: realSignal.link
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 如果是组,需要渲染空白的一行
|
||||||
|
renderSignals.push({
|
||||||
|
name: '',
|
||||||
|
kind: '',
|
||||||
|
ref: ''
|
||||||
|
});
|
||||||
|
// 如果没有关闭,把所有子节点加入其中
|
||||||
|
if (view.groupInfo.collapse === false) {
|
||||||
|
for (const child of view.children) {
|
||||||
|
const realSignal = link2CurrentWires.get(child.signalInfo.link);
|
||||||
|
renderSignals.push({
|
||||||
|
name: realSignal.name,
|
||||||
|
kind: realSignal.kind,
|
||||||
|
ref: realSignal.link
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const ilen = height / yStep;
|
const ilen = height / yStep;
|
||||||
const iskip = yOffset / yStep;
|
const iskip = yOffset / yStep;
|
||||||
@ -52,7 +91,7 @@ function* renderValues(desc, pstate) {
|
|||||||
|
|
||||||
let ifirst = 0;
|
let ifirst = 0;
|
||||||
for (let i = 0; i < ilen; ++ i) {
|
for (let i = 0; i < ilen; ++ i) {
|
||||||
const lane = view[i];
|
const lane = renderSignals[i];
|
||||||
if (lane && (lane.name || lane.kind)) {
|
if (lane && (lane.name || lane.kind)) {
|
||||||
if (i > iskip) {
|
if (i > iskip) {
|
||||||
break;
|
break;
|
||||||
@ -67,7 +106,7 @@ function* renderValues(desc, pstate) {
|
|||||||
ml.push(markers);
|
ml.push(markers);
|
||||||
|
|
||||||
for (let i = 0; i < (iskip + ilen); i++) {
|
for (let i = 0; i < (iskip + ilen); i++) {
|
||||||
const lane = view[i + (ifirst | 0)];
|
const lane = renderSignals[i + (ifirst | 0)];
|
||||||
|
|
||||||
if (lane && lane.kind === 'DIZ') {
|
if (lane && lane.kind === 'DIZ') {
|
||||||
markers.push(['g', tt(0, Math.round((i - (iskip - ifirst) + 1.18) * yStep))].concat(water(lane, desc, pstate)));
|
markers.push(['g', tt(0, Math.round((i - (iskip - ifirst) + 1.18) * yStep))].concat(water(lane, desc, pstate)));
|
||||||
@ -76,7 +115,9 @@ function* renderValues(desc, pstate) {
|
|||||||
} else if (lane && lane.ref) {
|
} else if (lane && lane.ref) {
|
||||||
const chango = desc.chango[lane.ref];
|
const chango = desc.chango[lane.ref];
|
||||||
if (chango && chango.kind === 'vec') {
|
if (chango && chango.kind === 'vec') {
|
||||||
const mLane = ['g', tt(0, Math.round((i - (iskip - ifirst) + 0.1) * yStep))];
|
// tt: 计算出当前这一行的所有 值 svg 在 Y 方向的偏移
|
||||||
|
const mLane = ['g', tt(0, Math.round((i - (iskip - ifirst) + 0.15) * yStep))];
|
||||||
|
|
||||||
const { wave } = chango;
|
const { wave } = chango;
|
||||||
const jlen = wave.length;
|
const jlen = wave.length;
|
||||||
|
|
||||||
@ -103,6 +144,7 @@ function* renderValues(desc, pstate) {
|
|||||||
// 宽度太小不画了
|
// 宽度太小不画了
|
||||||
if (w > 8) {
|
if (w > 8) {
|
||||||
const x = Math.round((xPreNorm + xCurNorm) / 2);
|
const x = Math.round((xPreNorm + xCurNorm) / 2);
|
||||||
|
// 计算 当前这一个 值 在 X 方向上的偏移
|
||||||
mLane.push(labeler(vPre, mPre, x, w));
|
mLane.push(labeler(vPre, mPre, x, w));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,8 +161,8 @@ function* renderValues(desc, pstate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// console.log(view);
|
// console.log(view);
|
||||||
for (let i = 0; i < view.length; i++) {
|
for (let i = 0; i < renderSignals.length; i++) {
|
||||||
const lane = view[i];
|
const lane = renderSignals[i];
|
||||||
if (lane && lane.vlines) {
|
if (lane && lane.vlines) {
|
||||||
markers.push(...vline(lane, pstate, i));
|
markers.push(...vline(lane, pstate, i));
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { globalSetting, globalStyle } from '../global';
|
import { globalSetting, globalStyle } from '../global';
|
||||||
|
|
||||||
import { gl_Colors, gl_Shifts, gl_Shifts_for_bar, gl_Shifts_map, gl_WidthShifts, barShift, getRatio, screenHeightPixel } from './render-utils.js';
|
import { gl_Colors, gl_Shifts, gl_Shifts_for_bar, gl_Shifts_map, gl_WidthShifts, barShift, getRatio, screenHeightPixel, maskColorIndexOffset } from './render-utils.js';
|
||||||
import { vertexShader, fragmentShader } from './render-shader.js';
|
import { vertexShader, fragmentShader } from './render-shader.js';
|
||||||
|
|
||||||
// const { ChangoItem } = require('./types.d.ts');
|
// const { ChangoItem } = require('./types.d.ts');
|
||||||
@ -23,6 +23,12 @@ class WebGL2WaveRender {
|
|||||||
this.pstate = pstate;
|
this.pstate = pstate;
|
||||||
this.plugins = plugins;
|
this.plugins = plugins;
|
||||||
|
|
||||||
|
// 用于在外部进行额外特性更新使用的一个类,未来需要往渲染管线中添加量可以随时往里面加东西,然后
|
||||||
|
// 在管线相关的函数中去使用它
|
||||||
|
this.renderPipeHook = {
|
||||||
|
userDefinedColor: undefined
|
||||||
|
};
|
||||||
|
|
||||||
const gl = canvas.getContext('webgl2', {
|
const gl = canvas.getContext('webgl2', {
|
||||||
premultipliedAlpha: false,
|
premultipliedAlpha: false,
|
||||||
alpha: true,
|
alpha: true,
|
||||||
@ -95,7 +101,7 @@ class WebGL2WaveRender {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {string} id
|
* @param {string} id 波形的 link
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* lineVertices: Uint32Array
|
* lineVertices: Uint32Array
|
||||||
* maskVertices: Uint32Array
|
* maskVertices: Uint32Array
|
||||||
@ -130,13 +136,23 @@ class WebGL2WaveRender {
|
|||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
translateValue2RenderParameter(value) {
|
translateValue2RenderParameter(value) {
|
||||||
|
let colorParam;
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 0: return { y: -1, color: 2 }; // 0 value
|
case 0: colorParam = { y: -1, color: 2 }; break; // 0 value
|
||||||
case 1: return { y: 1, color: 3 }; // 1 value
|
case 1: colorParam = { y: 1, color: 3 }; break; // 1 value
|
||||||
case 2: case 3: return { y: -1, color: 4 }; // 不定态 x
|
case 2: case 3: colorParam = { y: -1, color: 4 }; break; // 不定态 x
|
||||||
case 4: case 5: return { y: 0, color: 2 }; // 高阻态 z
|
case 4: case 5: colorParam = { y: 0, color: 2 }; break; // 高阻态 z
|
||||||
default: return { y: -1, color: 7 }; // 其他,我也不知道还有啥
|
default: colorParam = { y: -1, color: 7 }; break; // 其他,我也不知道还有啥
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value === 0 || value === 1) {
|
||||||
|
const renderPipeHook = this.renderPipeHook;
|
||||||
|
if (renderPipeHook.userDefinedColor !== undefined) {
|
||||||
|
colorParam.color = renderPipeHook.userDefinedColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return colorParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -265,17 +281,6 @@ class WebGL2WaveRender {
|
|||||||
const lineVertices = [];
|
const lineVertices = [];
|
||||||
const maskVertices = [];
|
const maskVertices = [];
|
||||||
|
|
||||||
// const lineVertices = [
|
|
||||||
// 0, 0, 2, 0,
|
|
||||||
// 10, 0, 2, 0,
|
|
||||||
// 10, 4, 2, 0,
|
|
||||||
// 20, 4, 2, 0,
|
|
||||||
// 20, 0, 2, 0,
|
|
||||||
// 30, 0, 2, 0,
|
|
||||||
// 30, 4, 2, 0
|
|
||||||
// ];
|
|
||||||
// return new Uint32Array(lineVertices);
|
|
||||||
|
|
||||||
|
|
||||||
// 制作 lineVertices
|
// 制作 lineVertices
|
||||||
for (let i = 0; i < pointNum; ++ i) {
|
for (let i = 0; i < pointNum; ++ i) {
|
||||||
@ -318,7 +323,7 @@ class WebGL2WaveRender {
|
|||||||
// 回退
|
// 回退
|
||||||
if (-- i > 0) {
|
if (-- i > 0) {
|
||||||
// 四元组: (x, yshift_index, color_index, width_shift_index)
|
// 四元组: (x, yshift_index, color_index, width_shift_index)
|
||||||
const rectangleVertices = this.makeRectangleVertices(p1.x, 1, perspectivePoints[i].x, -1, p1.color + 10, 4);
|
const rectangleVertices = this.makeRectangleVertices(p1.x, 1, perspectivePoints[i].x, -1, p1.color + maskColorIndexOffset, 4);
|
||||||
// 三角图元画矩形
|
// 三角图元画矩形
|
||||||
maskVertices.push(...rectangleVertices);
|
maskVertices.push(...rectangleVertices);
|
||||||
continue;
|
continue;
|
||||||
@ -335,7 +340,7 @@ class WebGL2WaveRender {
|
|||||||
if (p2.y > p1.y) {
|
if (p2.y > p1.y) {
|
||||||
// 矩形的四个点
|
// 矩形的四个点
|
||||||
// 四元组: (x, yshift_index, color_index, width_shift_index)
|
// 四元组: (x, yshift_index, color_index, width_shift_index)
|
||||||
const rectangleVertices = this.makeRectangleVertices(p2.x, p2.y, p3.x, p1.y, p2.color + 10, 4);
|
const rectangleVertices = this.makeRectangleVertices(p2.x, p2.y, p3.x, p1.y, p2.color + maskColorIndexOffset, 4);
|
||||||
// 三角图元画矩形
|
// 三角图元画矩形
|
||||||
maskVertices.push(...rectangleVertices);
|
maskVertices.push(...rectangleVertices);
|
||||||
}
|
}
|
||||||
@ -354,8 +359,6 @@ class WebGL2WaveRender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Array<string | number>} wave
|
* @param {Array<string | number>} wave
|
||||||
@ -369,6 +372,7 @@ class WebGL2WaveRender {
|
|||||||
const lineVertices = [];
|
const lineVertices = [];
|
||||||
const maskVertices = [];
|
const maskVertices = [];
|
||||||
const length = wave.length;
|
const length = wave.length;
|
||||||
|
const renderPipeHook = this.renderPipeHook;
|
||||||
|
|
||||||
for (let i = 0; i < length; ++ i) {
|
for (let i = 0; i < length; ++ i) {
|
||||||
const [t1, val, mask] = wave[i];
|
const [t1, val, mask] = wave[i];
|
||||||
@ -384,7 +388,14 @@ class WebGL2WaveRender {
|
|||||||
const a2 = {x: t1, y: 1};
|
const a2 = {x: t1, y: 1};
|
||||||
const a3 = {x: t2, y: 2};
|
const a3 = {x: t2, y: 2};
|
||||||
|
|
||||||
const color = mask ? 4 : 5;
|
let color;
|
||||||
|
if (mask) {
|
||||||
|
color = 4;
|
||||||
|
} else if (renderPipeHook.userDefinedColor !== undefined){
|
||||||
|
color = renderPipeHook.userDefinedColor;
|
||||||
|
} else {
|
||||||
|
color = 5;
|
||||||
|
}
|
||||||
|
|
||||||
const points = [ a1, p1, a3, a2, p0, a0 ];
|
const points = [ a1, p1, a3, a2, p0, a0 ];
|
||||||
const wsIndice = [ 1, 2, 3, 5, 6, 7 ];
|
const wsIndice = [ 1, 2, 3, 5, 6, 7 ];
|
||||||
@ -415,9 +426,9 @@ class WebGL2WaveRender {
|
|||||||
const point2 = points[i2];
|
const point2 = points[i2];
|
||||||
const point3 = points[i3];
|
const point3 = points[i3];
|
||||||
maskVertices.push(
|
maskVertices.push(
|
||||||
point1.x, point1.y, color + 10, wsIndice[i1],
|
point1.x, point1.y, color + maskColorIndexOffset, wsIndice[i1],
|
||||||
point2.x, point2.y, color + 10, wsIndice[i2],
|
point2.x, point2.y, color + maskColorIndexOffset, wsIndice[i2],
|
||||||
point3.x, point3.y, color + 10, wsIndice[i3]
|
point3.x, point3.y, color + maskColorIndexOffset, wsIndice[i3]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -470,6 +481,41 @@ class WebGL2WaveRender {
|
|||||||
gl.enableVertexAttribArray(webglLocation.pos);
|
gl.enableVertexAttribArray(webglLocation.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 用于重置 VAO 的函数
|
||||||
|
* @param {string} id 波形的 link
|
||||||
|
*/
|
||||||
|
resetVertice(id) {
|
||||||
|
const webglLocation = this.webglLocation;
|
||||||
|
const gl = webglLocation.gl;
|
||||||
|
const signalItem = this.globalLookup.chango[id];
|
||||||
|
|
||||||
|
const { lineVertices, maskVertices } = this.makeVertexByID(id);
|
||||||
|
this.lineVerticesMap.set(id, lineVertices);
|
||||||
|
this.maskVerticesMap.set(id, maskVertices);
|
||||||
|
|
||||||
|
// 清除自定义标志位
|
||||||
|
this.renderPipeHook.userDefinedColor = undefined;
|
||||||
|
|
||||||
|
// 创建并设置 绘制wave轮廓 主体轮廓的 缓冲区、vao、顶点设置
|
||||||
|
const lineVertexBuffer = gl.createBuffer();
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, lineVertexBuffer);
|
||||||
|
gl.bufferData(gl.ARRAY_BUFFER, lineVertices, gl.STATIC_DRAW);
|
||||||
|
signalItem.lineVao = gl.createVertexArray();
|
||||||
|
gl.bindVertexArray(signalItem.lineVao);
|
||||||
|
gl.vertexAttribIPointer(webglLocation.pos, 4, gl.UNSIGNED_INT, 0, 0);
|
||||||
|
gl.enableVertexAttribArray(webglLocation.pos);
|
||||||
|
|
||||||
|
// 创建并设置 绘制wave半透明遮罩层 主体轮廓的 缓冲区、vao、顶点设置
|
||||||
|
const maskVertexBuffer = gl.createBuffer();
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, maskVertexBuffer);
|
||||||
|
gl.bufferData(gl.ARRAY_BUFFER, maskVertices, gl.STATIC_DRAW);
|
||||||
|
signalItem.maskVao = gl.createVertexArray();
|
||||||
|
gl.bindVertexArray(signalItem.maskVao);
|
||||||
|
gl.vertexAttribIPointer(webglLocation.pos, 4, gl.UNSIGNED_INT, 0, 0);
|
||||||
|
gl.enableVertexAttribArray(webglLocation.pos);
|
||||||
|
}
|
||||||
|
|
||||||
initData() {
|
initData() {
|
||||||
if (globalSetting.prerender) {
|
if (globalSetting.prerender) {
|
||||||
for (const id of Reflect.ownKeys(this.globalLookup.chango)) {
|
for (const id of Reflect.ownKeys(this.globalLookup.chango)) {
|
||||||
@ -485,7 +531,7 @@ class WebGL2WaveRender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @description 更新默认波形的颜色
|
||||||
* @param {Array<Updater>} updaters
|
* @param {Array<Updater>} updaters
|
||||||
* @param {{
|
* @param {{
|
||||||
* updateMask: boolean
|
* updateMask: boolean
|
||||||
@ -504,7 +550,7 @@ class WebGL2WaveRender {
|
|||||||
gl_Colors[startIndex + 3] = rgba.alpha;
|
gl_Colors[startIndex + 3] = rgba.alpha;
|
||||||
|
|
||||||
if (config.updateMask) {
|
if (config.updateMask) {
|
||||||
const maskIndex = (updater.index + 10) * 4;
|
const maskIndex = (updater.index + maskColorIndexOffset) * 4;
|
||||||
gl_Colors[maskIndex] = rgba.red;
|
gl_Colors[maskIndex] = rgba.red;
|
||||||
gl_Colors[maskIndex + 1] = rgba.green;
|
gl_Colors[maskIndex + 1] = rgba.green;
|
||||||
gl_Colors[maskIndex + 2] = rgba.blue;
|
gl_Colors[maskIndex + 2] = rgba.blue;
|
||||||
@ -544,7 +590,7 @@ class WebGL2WaveRender {
|
|||||||
const plugins = this.plugins;
|
const plugins = this.plugins;
|
||||||
|
|
||||||
pstate.yStep = globalSetting.displaySignalHeight + globalStyle.sideBarItemMargin;
|
pstate.yStep = globalSetting.displaySignalHeight + globalStyle.sideBarItemMargin;
|
||||||
pstate.yDuty = (1 - 2 * globalStyle.sideBarItemMargin / this.pstate.yStep) * 0.9;
|
pstate.yDuty = (1 - 2 * globalStyle.sideBarItemMargin / this.pstate.yStep) * 0.95;
|
||||||
|
|
||||||
document.body.style.setProperty('--vcd-render-padding', pstate.topBarHeight + 'px');
|
document.body.style.setProperty('--vcd-render-padding', pstate.topBarHeight + 'px');
|
||||||
const canvasHeight = pstate.height - pstate.topBarHeight - pstate.botBarHeight;
|
const canvasHeight = pstate.height - pstate.topBarHeight - pstate.botBarHeight;
|
||||||
@ -595,15 +641,35 @@ class WebGL2WaveRender {
|
|||||||
// 清楚颜色缓冲区,也就是删除上一次的渲染结果
|
// 清楚颜色缓冲区,也就是删除上一次的渲染结果
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
// 根据 globalLookup 当前激活的需要渲染的信号进行渲染
|
// 根据 currentWiresRenderView 视图渲染
|
||||||
let index = 0;
|
const renderSignals = [];
|
||||||
for (const signal of globalLookup.currentWires) {
|
const link2CurrentWires = globalLookup.link2CurrentWires;
|
||||||
|
for (const view of globalLookup.currentWiresRenderView) {
|
||||||
|
if (view.renderType === 0) {
|
||||||
|
const realSignal = link2CurrentWires.get(view.signalInfo.link);
|
||||||
|
renderSignals.push(realSignal);
|
||||||
|
} else {
|
||||||
|
// 如果是组,需要渲染空白的一行
|
||||||
|
renderSignals.push(0);
|
||||||
|
// 如果没有关闭,把所有子节点加入其中
|
||||||
|
if (view.groupInfo.collapse === false) {
|
||||||
|
for (const child of view.children) {
|
||||||
|
const realSignal = link2CurrentWires.get(child.signalInfo.link);
|
||||||
|
renderSignals.push(realSignal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (let index = 0; index < renderSignals.length; ++ index) {
|
||||||
|
const signal = renderSignals[index];
|
||||||
|
if (signal === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const id = signal.link;
|
const id = signal.link;
|
||||||
const wave = globalLookup.chango[id].wave;
|
const wave = globalLookup.chango[id].wave;
|
||||||
|
|
||||||
// _this.makeBitVertex(wave, globalLookup.time, true);
|
|
||||||
// _this.makeVecVertex(wave, globalLookup.time, true);
|
|
||||||
|
|
||||||
const signalItem = globalLookup.chango[id];
|
const signalItem = globalLookup.chango[id];
|
||||||
if (!signalItem) {
|
if (!signalItem) {
|
||||||
return;
|
return;
|
||||||
@ -638,7 +704,6 @@ class WebGL2WaveRender {
|
|||||||
gl.drawArrays(gl.TRIANGLES, 0, maskVertices.length / 4);
|
gl.drawArrays(gl.TRIANGLES, 0, maskVertices.length / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
index ++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.map(fn => fn(globalLookup, pstate, elements));
|
plugins.map(fn => fn(globalLookup, pstate, elements));
|
||||||
|
@ -7,23 +7,21 @@ const yOffsetUpdate = (pstate, nextOffsetYFn) => {
|
|||||||
const currentRenderHeight = globalLookup.currentWires.size * pstate.yStep;
|
const currentRenderHeight = globalLookup.currentWires.size * pstate.yStep;
|
||||||
const canvasHeight = pstate.height - pstate.topBarHeight - pstate.botBarHeight;
|
const canvasHeight = pstate.height - pstate.topBarHeight - pstate.botBarHeight;
|
||||||
// console.log(currentRenderHeight, canvasHeight);
|
// console.log(currentRenderHeight, canvasHeight);
|
||||||
const maxOffsetX = Math.max(-40, currentRenderHeight - canvasHeight); // maximum offset
|
const maxOffsetX = Math.max(-20, currentRenderHeight - canvasHeight); // maximum offset
|
||||||
nextOffsetY = Math.min(nextOffsetY, maxOffsetX);
|
nextOffsetY = Math.min(nextOffsetY, maxOffsetX);
|
||||||
|
|
||||||
const minOffsetX = -40; // minimum offset
|
const minOffsetX = -20; // minimum offset
|
||||||
nextOffsetY = Math.max(nextOffsetY, minOffsetX);
|
nextOffsetY = Math.max(nextOffsetY, minOffsetX);
|
||||||
|
|
||||||
if (nextOffsetY === xOffset) {
|
if (nextOffsetY === xOffset) {
|
||||||
return false; // exit without scroll
|
return false; // exit without scroll
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('next offset y', nextOffsetY);
|
|
||||||
|
|
||||||
pstate.oldYOffset = pstate.yOffset;
|
pstate.oldYOffset = pstate.yOffset;
|
||||||
pstate.yOffset = nextOffsetY;
|
pstate.yOffset = nextOffsetY;
|
||||||
|
|
||||||
// 这玩意儿初始设置 -40 的偏移,当作 padding-top
|
// 这玩意儿初始设置 -20 的偏移,当作 padding-top
|
||||||
globalStyle.yOffset = nextOffsetY + 40;
|
globalStyle.yOffset = nextOffsetY + 20;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
"display-signal-info-scope.width": "width",
|
"display-signal-info-scope.width": "width",
|
||||||
"display-signal-info-scope.parent": "parent",
|
"display-signal-info-scope.parent": "parent",
|
||||||
|
|
||||||
"wavecolor": "color of wave",
|
"wavecolor": "default color of wave",
|
||||||
"wavecolor.normal-bit": "wave of one width",
|
"wavecolor.normal-bit": "wave of one width",
|
||||||
"wavecolor.normal-vec": "wave of more than one width",
|
"wavecolor.normal-vec": "wave of more than one width",
|
||||||
"wavecolor.high-impedance": "wave of high impedance",
|
"wavecolor.high-impedance": "wave of high impedance",
|
||||||
@ -54,6 +54,39 @@
|
|||||||
|
|
||||||
"loading": "loading",
|
"loading": "loading",
|
||||||
|
|
||||||
|
"context-menu.create-group": "create group",
|
||||||
|
"context-menu.join-group": "join created group",
|
||||||
|
"context-menu.change-color": "change color",
|
||||||
|
"context-menu.delete": "delete signal",
|
||||||
|
|
||||||
|
"context-menu.signal.name": "signal name",
|
||||||
|
"context-menu.signal.type": "signal type",
|
||||||
|
"context-menu.signal.width": "signal width",
|
||||||
|
"context-menu.signal.dep": "signal dependency",
|
||||||
|
|
||||||
|
"context-menu.group.cancel": "cancel group",
|
||||||
|
"context-menu.group.delete": "delete group",
|
||||||
|
|
||||||
|
"context-menu.group.empty": "No groups are currently available",
|
||||||
|
"context-menu.group.uname-group": "unamed group",
|
||||||
|
|
||||||
|
"toolbar.modal.common-digital": "Digital",
|
||||||
|
"toolbar.modal.ladder-analog": "Analog (Ladder)",
|
||||||
|
"toolbar.modal.line-analog": "Analog (Line)",
|
||||||
|
|
||||||
|
"toolbar.search.name": "Name",
|
||||||
|
"toolbar.search.value": "Value",
|
||||||
|
|
||||||
|
"toolbar.format.category.base": "Base",
|
||||||
|
"toolbar.format.category.dec": "Decimal",
|
||||||
|
"toolbar.format.category.float": "Float",
|
||||||
|
|
||||||
|
"toolbar.format.signed": "Signed",
|
||||||
|
"toolbar.format.unsigned": "Unsigned",
|
||||||
|
"toolbar.format.half": "Half (16bit)",
|
||||||
|
"toolbar.format.float": "Float (32bit)",
|
||||||
|
"toolbar.format.double": "Double (64bit)",
|
||||||
|
|
||||||
"current-version": "current version",
|
"current-version": "current version",
|
||||||
"copyright": "The copyright of this software belongs to <a href=\"https://github.com/Digital-EDA\" target=\"_blank\">Digital-IDE</a> project team. Welcome to <a href=\"https://github.com/Digital-EDA/Digital-IDE\">Star</a>."
|
"copyright": "The copyright of this software belongs to <a href=\"https://github.com/Digital-EDA\" target=\"_blank\">Digital-IDE</a> project team. Welcome to <a href=\"https://github.com/Digital-EDA/Digital-IDE\">Star</a>."
|
||||||
}
|
}
|
@ -16,7 +16,7 @@
|
|||||||
"search-mode": "搜索模式",
|
"search-mode": "搜索模式",
|
||||||
"search-scope": "搜索范围",
|
"search-scope": "搜索范围",
|
||||||
"search-display-parent-only": "只展示父模块",
|
"search-display-parent-only": "只展示父模块",
|
||||||
"search-nothing": "没有找到任何符号",
|
"search-nothing": "没有找到任何信号",
|
||||||
|
|
||||||
"signal-only": "信号",
|
"signal-only": "信号",
|
||||||
"module-only": "模块",
|
"module-only": "模块",
|
||||||
@ -29,7 +29,7 @@
|
|||||||
"display-signal-info-scope.width": "位宽",
|
"display-signal-info-scope.width": "位宽",
|
||||||
"display-signal-info-scope.parent": "所属模块名",
|
"display-signal-info-scope.parent": "所属模块名",
|
||||||
|
|
||||||
"wavecolor": "波形颜色",
|
"wavecolor": "默认波形颜色",
|
||||||
"wavecolor.normal-bit": "单位宽波形",
|
"wavecolor.normal-bit": "单位宽波形",
|
||||||
"wavecolor.normal-vec": "多位宽波形",
|
"wavecolor.normal-vec": "多位宽波形",
|
||||||
"wavecolor.high-impedance": "高阻态波形",
|
"wavecolor.high-impedance": "高阻态波形",
|
||||||
@ -52,6 +52,41 @@
|
|||||||
|
|
||||||
"loading": "加载中",
|
"loading": "加载中",
|
||||||
|
|
||||||
|
"context-menu.create-group": "新建组",
|
||||||
|
"context-menu.join-group": "加入已有分组",
|
||||||
|
"context-menu.change-color": "修改颜色",
|
||||||
|
"context-menu.delete": "删除信号",
|
||||||
|
|
||||||
|
"context-menu.signal.name": "信号名称",
|
||||||
|
"context-menu.signal.type": "信号类型",
|
||||||
|
"context-menu.signal.width": "信号宽度",
|
||||||
|
"context-menu.signal.dep": "依赖关系",
|
||||||
|
|
||||||
|
"context-menu.group.cancel": "取消分组",
|
||||||
|
"context-menu.group.delete": "删除分组",
|
||||||
|
|
||||||
|
"context-menu.group.empty": "当前没有可用的分组",
|
||||||
|
"context-menu.group.uname-group": "未命名分组",
|
||||||
|
|
||||||
|
"toolbar.modal.common-digital": "数字",
|
||||||
|
"toolbar.modal.ladder-analog": "模拟(阶梯)",
|
||||||
|
"toolbar.modal.line-analog": "模拟(折线)",
|
||||||
|
|
||||||
|
"toolbar.search.name": "名称",
|
||||||
|
"toolbar.search.value": "值",
|
||||||
|
|
||||||
|
"toolbar.format.category.base": "基础",
|
||||||
|
"toolbar.format.category.dec": "十进制",
|
||||||
|
"toolbar.format.category.float": "浮点数",
|
||||||
|
|
||||||
|
"toolbar.format.signed": "有符号",
|
||||||
|
"toolbar.format.unsigned": "无符号",
|
||||||
|
"toolbar.format.half": "半精度(16bit)",
|
||||||
|
"toolbar.format.float": "单精度(32bit)",
|
||||||
|
"toolbar.format.double": "双精度(64bit)",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"current-version": "当前版本",
|
"current-version": "当前版本",
|
||||||
"copyright": "本软件版权归 <a href=\"https://github.com/Digital-EDA\" target=\"_blank\">Digital-IDE</a> 项目组所有,欢迎 <a href=\"https://github.com/Digital-EDA/Digital-IDE\">Star</a>。"
|
"copyright": "本软件版权归 <a href=\"https://github.com/Digital-EDA\" target=\"_blank\">Digital-IDE</a> 项目组所有,欢迎 <a href=\"https://github.com/Digital-EDA/Digital-IDE\">Star</a>。"
|
||||||
}
|
}
|
@ -20,13 +20,23 @@
|
|||||||
* @typedef {Record<string, ChangoItem>} Chango
|
* @typedef {Record<string, ChangoItem>} Chango
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description
|
||||||
|
* @typedef {Object} WireItemBaseInfo
|
||||||
|
* @property {string} link
|
||||||
|
* @property {string} name
|
||||||
|
* @property {number} size
|
||||||
|
* @property {string | undefined} parentLink 如果当前的波形被加入了一个 group 或 bus 中,该值则不会为 undefined
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description
|
* @description
|
||||||
* @typedef {object} WaveRenderSidebarItem
|
* @typedef {Object} WaveRenderSidebarItem
|
||||||
* @property {WireItem} signal
|
* @property {WireItemBaseInfo} signalInfo
|
||||||
|
* @property {GroupBaseInfo} groupInfo
|
||||||
* @property {0 | 1} renderType 0 代表单条波形,1 代表 分组
|
* @property {0 | 1} renderType 0 代表单条波形,1 代表 分组
|
||||||
* @property {WireItem[]} children 分组内的波形,只有当 renderType 为 1 时才不为空数组
|
* @property {WaveRenderSidebarItem[]} children 分组内的波形,只有当 renderType 为 1 时才不为空数组
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,6 +50,25 @@
|
|||||||
* @property {string} type
|
* @property {string} type
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description
|
||||||
|
* @typedef {Object} GroupBaseInfo
|
||||||
|
* @property {string} name
|
||||||
|
* @property {string} color
|
||||||
|
* @property {boolean} collapse
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description
|
||||||
|
* @typedef {Object} TopModWireItem
|
||||||
|
* @property {string} kind
|
||||||
|
* @property {string} link
|
||||||
|
* @property {string} name
|
||||||
|
* @property {number} size
|
||||||
|
* @property {string} type
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description
|
* @description
|
||||||
* @typedef {Array<{ ref: string }>} View
|
* @typedef {Array<{ ref: string }>} View
|
||||||
@ -56,6 +85,10 @@
|
|||||||
* @property {Chango} chango
|
* @property {Chango} chango
|
||||||
* @property {View} view
|
* @property {View} view
|
||||||
* @property {CurrentWires} currentWires
|
* @property {CurrentWires} currentWires
|
||||||
|
* @property {Map<string, WireItem>} link2CurrentWires
|
||||||
|
* @property {WaveRenderSidebarItem[]} currentWiresRenderView
|
||||||
|
* @property {Record<string, BigInt | string>} currentSignalValues
|
||||||
|
* @property {Set<string>} sidebarSelectedWireLinks
|
||||||
* @property {number} time
|
* @property {number} time
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -108,3 +141,13 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef {'scrollLeft' | 'scrollRight' | 'scrollUp' | 'scrollDown' | 'scaleUp' | 'scaleDown'} EventHandlerKind
|
* @typedef {'scrollLeft' | 'scrollRight' | 'scrollUp' | 'scrollDown' | 'scaleUp' | 'scaleDown'} EventHandlerKind
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} IRenderOption
|
||||||
|
* @property {number | undefined} height 波形的高度,默认为 30,可以在右侧的栏目中设置
|
||||||
|
* @property {string | undefined} color 波形的颜色,可以在右击菜单栏中设置
|
||||||
|
* @property {number | undefined} valueFormat 波形数字的展示形式,可以在上侧的下拉菜单中设置,vec 类型的波形,可以设置为:0:二进制、1:八进制、2:十六进制、3::有符号整型、4:无符号整型、5:浮点数(半精度、6:单精度、7:双精度)
|
||||||
|
* @property {number | undefined} renderModal 波形的渲染模式,默认为 bit 和 vec,vec 类型的波形,可以设置为:0:数字形式、1:折线模拟形式、2:阶梯模拟形式
|
||||||
|
*/
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user