@@ -136,6 +136,8 @@ export struct CustomKeyOverlay {
136136 @State private snapEnabled: boolean = false;
137137 /** 吸附阈值 (vp) */
138138 private static readonly SNAP_THRESHOLD_VP = 8;
139+ /** 编辑工具栏避让按键时保留的缓冲距离 (vp) */
140+ private static readonly EDIT_TOOLBAR_AVOID_MARGIN_VP = 12;
139141 /** 对齐辅助线位置 (vp),-1 表示隐藏 */
140142 @State private alignGuideX: number = -1;
141143 @State private alignGuideY: number = -1;
@@ -145,6 +147,9 @@ export struct CustomKeyOverlay {
145147 @State private groupDragKeyId: string = ''; // 正在被直接拖拽的键 ID
146148 @State private groupDragX: number = 0;
147149 @State private groupDragY: number = 0;
150+ /** 正在拖拽的按键,用于让编辑工具栏避让当前操作区域 */
151+ @State private activeDragKeyId: string = '';
152+ @State private activeDragY: number = 0;
148153
149154 /** 二维码分享面板 */
150155 @State private showSharePanel: boolean = false;
@@ -204,6 +209,90 @@ export struct CustomKeyOverlay {
204209 return this.keys.slice().sort((a: CustomKeyDef, b: CustomKeyDef) => a.layer - b.layer);
205210 }
206211
212+ private isEditToolbarVisible(): boolean {
213+ return this.editMode &&
214+ !this.showAddPanel &&
215+ !this.showPropertyPanel &&
216+ !this.showSharePanel &&
217+ !this.showThemePanel &&
218+ !this.showProfilePanel &&
219+ !this.showAiPanel;
220+ }
221+
222+ private getToolbarTargetKeyId(): string {
223+ return this.activeDragKeyId !== '' ? this.activeDragKeyId : this.selectedKeyId;
224+ }
225+
226+ private getToolbarTargetGroupId(): string {
227+ const keyId = this.getToolbarTargetKeyId();
228+ if (keyId === '') return '';
229+ const key = this.keys.find((item: CustomKeyDef): boolean => item.id === keyId);
230+ return key?.groupId ?? '';
231+ }
232+
233+ private isToolbarTargetKey(key: CustomKeyDef): boolean {
234+ const keyId = this.getToolbarTargetKeyId();
235+ if (keyId === '') return false;
236+ if (key.id === keyId) return true;
237+ const groupId = this.getToolbarTargetGroupId();
238+ return groupId !== '' && key.groupId === groupId;
239+ }
240+
241+ private getToolbarKeyVisualDy(key: CustomKeyDef): number {
242+ if (this.activeDragKeyId === '') return 0;
243+ if (key.id === this.activeDragKeyId) return this.activeDragY;
244+ const groupId = this.getToolbarTargetGroupId();
245+ if (groupId !== '' && key.groupId === groupId) {
246+ return this.activeDragY;
247+ }
248+ return 0;
249+ }
250+
251+ private getToolbarDockOverlapScore(dockTop: boolean, targetOnly: boolean): number {
252+ if (this.screenHeightVp <= 0) return 0;
253+ const margin = CustomKeyOverlay.EDIT_TOOLBAR_AVOID_MARGIN_VP;
254+ const bandTop = dockTop ? 0 : Math.max(0, this.screenHeightVp - EDIT_TOOLBAR_HEIGHT - margin);
255+ const bandBottom = dockTop ? Math.min(this.screenHeightVp, EDIT_TOOLBAR_HEIGHT + margin) : this.screenHeightVp;
256+ let score = 0;
257+
258+ for (const key of this.keys) {
259+ if (targetOnly && !this.isToolbarTargetKey(key)) continue;
260+ const dy = this.getToolbarKeyVisualDy(key);
261+ const keyTop = key.yPercent * this.screenHeightVp - key.height / 2 + dy;
262+ const keyBottom = key.yPercent * this.screenHeightVp + key.height / 2 + dy;
263+ const overlapTop = Math.max(keyTop, bandTop);
264+ const overlapBottom = Math.min(keyBottom, bandBottom);
265+ if (overlapBottom > overlapTop) {
266+ score += overlapBottom - overlapTop;
267+ }
268+ }
269+
270+ return score;
271+ }
272+
273+ private shouldDockToolbarToTop(): boolean {
274+ if (this.screenHeightVp <= 0) return false;
275+
276+ const bottomTargetScore = this.getToolbarDockOverlapScore(false, true);
277+ const topTargetScore = this.getToolbarDockOverlapScore(true, true);
278+ if (bottomTargetScore !== topTargetScore) {
279+ return topTargetScore < bottomTargetScore;
280+ }
281+
282+ const bottomScore = this.getToolbarDockOverlapScore(false, false);
283+ const topScore = this.getToolbarDockOverlapScore(true, false);
284+ if (bottomScore !== topScore) {
285+ return topScore < bottomScore;
286+ }
287+
288+ return false;
289+ }
290+
291+ private getEditToolbarY(): number {
292+ if (this.shouldDockToolbarToTop()) return 0;
293+ return Math.max(0, this.screenHeightVp - EDIT_TOOLBAR_HEIGHT);
294+ }
295+
207296 /** 屏幕变化监听回调引用,用于注销 */
208297 private displayChangeCallback: ((displayId: number) => void) | null = null;
209298
@@ -262,7 +351,7 @@ export struct CustomKeyOverlay {
262351 }
263352
264353 build() {
265- Column () {
354+ Stack () {
266355 // 主区域
267356 Stack() {
268357 // 对齐辅助线(拖拽吸附时显示)
@@ -398,6 +487,8 @@ export struct CustomKeyOverlay {
398487 this.onKeyAction(action, isDown);
399488 },
400489 onGroupDragMove: (keyId: string, deltaX: number, deltaY: number): void => {
490+ this.activeDragKeyId = deltaX === 0 && deltaY === 0 ? '' : keyId;
491+ this.activeDragY = deltaY;
401492 // 更新组拖拽视觉偏移
402493 const dragKey = this.keys.find(k => k.id === keyId);
403494 if (dragKey && dragKey.groupId) {
@@ -417,6 +508,8 @@ export struct CustomKeyOverlay {
417508 this.groupDragKeyId = '';
418509 this.groupDragX = 0;
419510 this.groupDragY = 0;
511+ this.activeDragKeyId = '';
512+ this.activeDragY = 0;
420513 this.alignGuideX = -1;
421514 this.alignGuideY = -1;
422515 this.handleDragEnd(keyId, deltaX, deltaY);
@@ -564,14 +657,14 @@ export struct CustomKeyOverlay {
564657 }
565658 }
566659 .width('100%')
567- .layoutWeight(1 )
660+ .height('100%' )
568661 .alignContent(Alignment.BottomStart)
569662 .hitTestBehavior(HitTestMode.Transparent)
570663 // hitTestReady: 微小 opacity 变化触发父层重渲染,刷新命中测试树
571664 .opacity(this.hidingForCapture ? 0 : (this.hitTestReady > 0 ? 1 : 0.99))
572665
573666 // 编辑模式工具栏(面板打开时隐藏,避免遮挡面板)
574- if (this.editMode && !this.showAddPanel && !this.showPropertyPanel && !this.showSharePanel && !this.showThemePanel && !this.showProfilePanel && !this.showAiPanel ) {
667+ if (this.isEditToolbarVisible() ) {
575668 Row({ space: 12 }) {
576669 Row({ space: 4 }) {
577670 SymbolGlyph($r('sys.symbol.plus_circle'))
@@ -728,6 +821,8 @@ export struct CustomKeyOverlay {
728821 .backgroundColor('#CC1A1A2E')
729822 .backdropBlur(30)
730823 .border({ width: { top: 1 }, color: '#20FFFFFF' })
824+ .position({ x: 0, y: this.getEditToolbarY() })
825+ .hitTestBehavior(HitTestMode.Transparent)
731826 }
732827 }
733828 .width('100%')
0 commit comments