@@ -125,12 +125,39 @@ export class SNLayoutBuilder {
125125 // 先计算 Block 的宽度(这样子节点 Section Block 可以获取父节点宽度)
126126 this . calculateNodeWidth ( scoreBlock ) ;
127127
128+ // 计算所有子节点(包括元信息行)的宽度和高度
129+ if ( scoreBlock . children ) {
130+ for ( const child of scoreBlock . children ) {
131+ this . calculateNodeWidth ( child ) ;
132+ this . calculateNodeHeight ( child ) ;
133+ }
134+ }
135+
128136 // 构建 Section 节点
129137 this . buildSections ( score . children || [ ] , scoreBlock ) ;
130138
131139 // 子节点构建完成后,计算 Score Block 的高度和位置
132140 this . calculateNodeHeight ( scoreBlock ) ;
133141 this . calculateNodePosition ( scoreBlock ) ;
142+
143+ // 计算所有子节点(包括元信息行和Section)的位置
144+ if ( scoreBlock . children ) {
145+ for ( const child of scoreBlock . children ) {
146+ // 先确保子节点的宽度和高度已计算
147+ this . calculateNodeWidth ( child ) ;
148+ this . calculateNodeHeight ( child ) ;
149+ // 计算子节点的位置
150+ this . calculateNodePosition ( child ) ;
151+ // 如果子节点是Line,需要递归计算其子节点(Element)的位置
152+ if ( child . children ) {
153+ for ( const grandChild of child . children ) {
154+ this . calculateNodeWidth ( grandChild ) ;
155+ this . calculateNodeHeight ( grandChild ) ;
156+ this . calculateNodePosition ( grandChild ) ;
157+ }
158+ }
159+ }
160+ }
134161 }
135162 }
136163
@@ -156,6 +183,14 @@ export class SNLayoutBuilder {
156183 // 先计算 Block 的宽度(这样子节点 VoiceGroup 可以获取父节点宽度)
157184 this . calculateNodeWidth ( sectionBlock ) ;
158185
186+ // 计算所有子节点(包括元信息行)的宽度和高度
187+ if ( sectionBlock . children ) {
188+ for ( const child of sectionBlock . children ) {
189+ this . calculateNodeWidth ( child ) ;
190+ this . calculateNodeHeight ( child ) ;
191+ }
192+ }
193+
159194 // 构建 VoiceGroup(包含所有 Voice,并处理分行逻辑)
160195 this . buildVoiceGroups (
161196 ( section . children || [ ] ) as SNParserNode [ ] ,
@@ -165,6 +200,25 @@ export class SNLayoutBuilder {
165200 // 子节点构建完成后,计算 Section Block 的高度和位置
166201 this . calculateNodeHeight ( sectionBlock ) ;
167202 this . calculateNodePosition ( sectionBlock ) ;
203+
204+ // 计算所有子节点(包括元信息行和VoiceGroup)的位置
205+ if ( sectionBlock . children ) {
206+ for ( const child of sectionBlock . children ) {
207+ // 先确保子节点的宽度和高度已计算
208+ this . calculateNodeWidth ( child ) ;
209+ this . calculateNodeHeight ( child ) ;
210+ // 计算子节点的位置
211+ this . calculateNodePosition ( child ) ;
212+ // 如果子节点是Line,需要递归计算其子节点(Element)的位置
213+ if ( child . children ) {
214+ for ( const grandChild of child . children ) {
215+ this . calculateNodeWidth ( grandChild ) ;
216+ this . calculateNodeHeight ( grandChild ) ;
217+ this . calculateNodePosition ( grandChild ) ;
218+ }
219+ }
220+ }
221+ }
168222 }
169223 }
170224
@@ -679,7 +733,28 @@ export class SNLayoutBuilder {
679733 node . layout . width > 0 ;
680734
681735 if ( ! hasFixedWidth ) {
682- if ( node . children && node . children . length > 0 ) {
736+ // 检查是否是元信息标题容器元素(需要撑满父级宽度)
737+ const nodeData = node . data as any ;
738+ const isMetadataTitleContainer =
739+ nodeData ?. type === 'metadata-title-container' ||
740+ nodeData ?. type === 'metadata-section-title-container' ;
741+
742+ if ( isMetadataTitleContainer && node . parent ) {
743+ // 元信息标题元素:撑满父级可用宽度
744+ const parentAvailableWidth = node . getParentAvailableWidth ( ) ;
745+ if ( parentAvailableWidth !== null && parentAvailableWidth > 0 ) {
746+ const padding = node . layout . padding || {
747+ top : 0 ,
748+ right : 0 ,
749+ bottom : 0 ,
750+ left : 0 ,
751+ } ;
752+ node . layout . width =
753+ parentAvailableWidth - padding . left - padding . right ;
754+ } else {
755+ node . layout . width = 0 ;
756+ }
757+ } else if ( node . children && node . children . length > 0 ) {
683758 const childrenMaxWidth = node . calculateChildrenMaxWidth ( ) ;
684759 const padding = node . layout . padding || {
685760 top : 0 ,
@@ -809,30 +884,83 @@ export class SNLayoutBuilder {
809884 // 对于垂直排列的节点(Block, Line),X = 父节点的padding.left
810885 // 对于水平排列的节点(Element),X = 父节点的padding.left + 前面兄弟节点的累积宽度
811886 if ( node instanceof SNLayoutElement ) {
812- // Element节点:水平排列,需要累加前面兄弟节点的宽度
813- let x = parentPadding . left ;
887+ // 检查是否是右对齐的元信息元素(如作词作曲)
888+ const nodeData = node . data as any ;
889+ const isRightAlignedMetadata =
890+ nodeData ?. type === 'metadata-contributors' &&
891+ nodeData ?. align === 'right' ;
892+
893+ if ( isRightAlignedMetadata && node . parent instanceof SNLayoutLine ) {
894+ // 右对齐的元信息元素:计算位置使其位于行的右侧
895+ // 确保父节点(Line)的宽度已计算
896+ if (
897+ ! parentLayout . width ||
898+ typeof parentLayout . width !== 'number' ||
899+ parentLayout . width === 0
900+ ) {
901+ // 如果父节点宽度未计算,先计算父节点宽度
902+ this . calculateNodeWidth ( node . parent ) ;
903+ }
814904
815- const siblingIndex = node . parent . children ?. indexOf ( node ) ?? - 1 ;
816- if ( siblingIndex > 0 && node . parent . children ) {
817- for ( let i = 0 ; i < siblingIndex ; i ++ ) {
818- const sibling = node . parent . children [ i ] ;
819- if ( sibling . layout ) {
820- const siblingWidth =
821- typeof sibling . layout . width === 'number'
822- ? sibling . layout . width
823- : 0 ;
824- const siblingMargin = sibling . layout . margin || {
825- top : 0 ,
826- right : 0 ,
827- bottom : 0 ,
828- left : 0 ,
829- } ;
830- // 累加兄弟节点的宽度和右边margin
831- x += siblingWidth + siblingMargin . right ;
905+ const parentWidth =
906+ typeof parentLayout . width === 'number' ? parentLayout . width : 0 ;
907+ const elementWidth =
908+ typeof node . layout . width === 'number' ? node . layout . width : 0 ;
909+ const elementPadding = node . layout . padding || {
910+ top : 0 ,
911+ right : 0 ,
912+ bottom : 0 ,
913+ left : 0 ,
914+ } ;
915+
916+ // 如果父节点宽度仍然为0,使用父节点的父节点宽度
917+ let actualParentWidth = parentWidth ;
918+ if ( actualParentWidth === 0 && node . parent . parent ?. layout ) {
919+ const grandParentWidth =
920+ typeof node . parent . parent . layout . width === 'number'
921+ ? node . parent . parent . layout . width
922+ : 0 ;
923+ if ( grandParentWidth > 0 ) {
924+ actualParentWidth = grandParentWidth ;
925+ }
926+ }
927+
928+ // x = 父节点可用宽度 - 元素宽度
929+ // 父节点可用宽度 = 父节点宽度 - 父节点padding.left - 父节点padding.right
930+ const parentAvailableWidth =
931+ actualParentWidth - parentPadding . left - parentPadding . right ;
932+ // 元素实际占用宽度 = 元素宽度 + 元素padding.left + 元素padding.right
933+ const elementTotalWidth =
934+ elementWidth + elementPadding . left + elementPadding . right ;
935+ // x = 父节点padding.left + (父节点可用宽度 - 元素实际占用宽度)
936+ node . layout . x =
937+ parentPadding . left + parentAvailableWidth - elementTotalWidth ;
938+ } else {
939+ // Element节点:水平排列,需要累加前面兄弟节点的宽度
940+ let x = parentPadding . left ;
941+
942+ const siblingIndex = node . parent . children ?. indexOf ( node ) ?? - 1 ;
943+ if ( siblingIndex > 0 && node . parent . children ) {
944+ for ( let i = 0 ; i < siblingIndex ; i ++ ) {
945+ const sibling = node . parent . children [ i ] ;
946+ if ( sibling . layout ) {
947+ const siblingWidth =
948+ typeof sibling . layout . width === 'number'
949+ ? sibling . layout . width
950+ : 0 ;
951+ const siblingMargin = sibling . layout . margin || {
952+ top : 0 ,
953+ right : 0 ,
954+ bottom : 0 ,
955+ left : 0 ,
956+ } ;
957+ // 累加兄弟节点的宽度和右边margin
958+ x += siblingWidth + siblingMargin . right ;
959+ }
832960 }
833961 }
962+ node . layout . x = x ;
834963 }
835- node . layout . x = x ;
836964 } else {
837965 // Block和Line节点:垂直排列,X = 父节点的padding.left
838966 node . layout . x = parentPadding . left ;
0 commit comments