feat: UI输入框IME支持和编辑器Inspector重构 (#310)
UI系统改进: - 添加 IMEHelper 支持中文/日文/韩文输入法 - UIInputFieldComponent 添加组合输入状态管理 - UIInputSystem 添加 IME 事件处理 - UIInputFieldRenderSystem 优化渲染逻辑 - UIRenderCollector 增强纹理处理 引擎改进: - EngineBridge 添加新的渲染接口 - EngineRenderSystem 优化渲染流程 - Rust 引擎添加新的渲染功能 编辑器改进: - 新增模块化 Inspector 组件架构 - EntityRefField 增强实体引用选择 - 优化 FlexLayoutDock 和 SceneHierarchy 样式 - 添加国际化文本
This commit is contained in:
@@ -293,6 +293,32 @@ export class EngineBridge implements ITextureEngineBridge {
|
||||
this.getEngine().renderOverlay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set scissor rect for clipping (screen coordinates, Y-down).
|
||||
* 设置裁剪矩形(屏幕坐标,Y 轴向下)。
|
||||
*
|
||||
* Content outside this rect will be clipped.
|
||||
* 此矩形外的内容将被裁剪。
|
||||
*
|
||||
* @param x - Left edge in screen coordinates | 屏幕坐标中的左边缘
|
||||
* @param y - Top edge in screen coordinates (Y-down) | 屏幕坐标中的上边缘(Y 向下)
|
||||
* @param width - Rect width | 矩形宽度
|
||||
* @param height - Rect height | 矩形高度
|
||||
*/
|
||||
setScissorRect(x: number, y: number, width: number, height: number): void {
|
||||
if (!this.initialized) return;
|
||||
this.getEngine().setScissorRect(x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear scissor rect (disable clipping).
|
||||
* 清除裁剪矩形(禁用裁剪)。
|
||||
*/
|
||||
clearScissorRect(): void {
|
||||
if (!this.initialized) return;
|
||||
this.getEngine().clearScissorRect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a texture.
|
||||
* 加载纹理。
|
||||
|
||||
@@ -51,6 +51,13 @@ export interface ProviderRenderData {
|
||||
materialIds?: Uint32Array;
|
||||
/** Material overrides (per-group). | 材质覆盖(按组)。 */
|
||||
materialOverrides?: MaterialOverrides;
|
||||
/**
|
||||
* Clip rectangle for scissor test (screen coordinates).
|
||||
* All primitives in this batch will be clipped to this rect.
|
||||
* 裁剪矩形用于 scissor test(屏幕坐标)。
|
||||
* 此批次中的所有原语将被裁剪到此矩形。
|
||||
*/
|
||||
clipRect?: { x: number; y: number; width: number; height: number };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -615,31 +622,97 @@ export class EngineRenderSystem extends EntitySystem {
|
||||
|
||||
this.bridge.pushScreenSpaceMode(canvasWidth, canvasHeight);
|
||||
|
||||
// Clear batcher for screen space content
|
||||
// 清空批处理器用于屏幕空间内容
|
||||
this.batcher.clear();
|
||||
// Group sprites by clipRect (in render order)
|
||||
// 按 clipRect 分组 sprites(按渲染顺序)
|
||||
type ClipGroup = {
|
||||
clipRect: { x: number; y: number; width: number; height: number } | undefined;
|
||||
sprites: SpriteRenderData[];
|
||||
};
|
||||
|
||||
// Submit screen space sprites
|
||||
// 提交屏幕空间 sprites
|
||||
const clipGroups: ClipGroup[] = [];
|
||||
let currentClipRect: { x: number; y: number; width: number; height: number } | undefined = undefined;
|
||||
let currentGroup: SpriteRenderData[] = [];
|
||||
|
||||
// Helper to check if two clip rects are equal
|
||||
// 辅助函数检查两个裁剪矩形是否相等
|
||||
const clipRectsEqual = (
|
||||
a: { x: number; y: number; width: number; height: number } | undefined,
|
||||
b: { x: number; y: number; width: number; height: number } | undefined
|
||||
): boolean => {
|
||||
if (a === b) return true;
|
||||
if (!a || !b) return false;
|
||||
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
||||
};
|
||||
|
||||
// Group sprites by consecutive clipRect
|
||||
// 按连续的 clipRect 分组 sprites
|
||||
for (const item of screenSpaceItems) {
|
||||
for (const sprite of item.sprites) {
|
||||
this.batcher.addSprite(sprite);
|
||||
const spriteClipRect = sprite.clipRect;
|
||||
|
||||
if (!clipRectsEqual(spriteClipRect, currentClipRect)) {
|
||||
// Save current group if not empty
|
||||
// 如果当前组不为空则保存
|
||||
if (currentGroup.length > 0) {
|
||||
clipGroups.push({ clipRect: currentClipRect, sprites: currentGroup });
|
||||
}
|
||||
// Start new group
|
||||
// 开始新组
|
||||
currentClipRect = spriteClipRect;
|
||||
currentGroup = [sprite];
|
||||
} else {
|
||||
currentGroup.push(sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.batcher.isEmpty) {
|
||||
const sprites = this.batcher.getSprites();
|
||||
|
||||
// Apply material overrides before rendering
|
||||
// 在渲染前应用材质覆盖
|
||||
this.applySpriteMaterialOverrides(sprites);
|
||||
|
||||
this.bridge.submitSprites(sprites);
|
||||
// Render overlay (without clearing screen)
|
||||
// 渲染叠加层(不清屏)
|
||||
this.bridge.renderOverlay();
|
||||
// Don't forget the last group
|
||||
// 别忘了最后一组
|
||||
if (currentGroup.length > 0) {
|
||||
clipGroups.push({ clipRect: currentClipRect, sprites: currentGroup });
|
||||
}
|
||||
|
||||
// Render each clip group
|
||||
// 渲染每个裁剪组
|
||||
for (const group of clipGroups) {
|
||||
// Set or clear scissor rect
|
||||
// 设置或清除裁剪矩形
|
||||
if (group.clipRect) {
|
||||
this.bridge.setScissorRect(
|
||||
group.clipRect.x,
|
||||
group.clipRect.y,
|
||||
group.clipRect.width,
|
||||
group.clipRect.height
|
||||
);
|
||||
} else {
|
||||
this.bridge.clearScissorRect();
|
||||
}
|
||||
|
||||
// Clear batcher and add sprites
|
||||
// 清空批处理器并添加 sprites
|
||||
this.batcher.clear();
|
||||
for (const sprite of group.sprites) {
|
||||
this.batcher.addSprite(sprite);
|
||||
}
|
||||
|
||||
if (!this.batcher.isEmpty) {
|
||||
const sprites = this.batcher.getSprites();
|
||||
|
||||
// Apply material overrides before rendering
|
||||
// 在渲染前应用材质覆盖
|
||||
this.applySpriteMaterialOverrides(sprites);
|
||||
|
||||
this.bridge.submitSprites(sprites);
|
||||
// Render overlay (without clearing screen)
|
||||
// 渲染叠加层(不清屏)
|
||||
this.bridge.renderOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
// Clear scissor rect after all groups
|
||||
// 所有组渲染完后清除裁剪矩形
|
||||
this.bridge.clearScissorRect();
|
||||
|
||||
// Restore world space camera
|
||||
// 恢复世界空间相机
|
||||
this.bridge.popScreenSpaceMode();
|
||||
@@ -802,6 +875,7 @@ export class EngineRenderSystem extends EntitySystem {
|
||||
// 检查材质数据
|
||||
const hasMaterialIds = data.materialIds && data.materialIds.length > 0;
|
||||
const hasMaterialOverrides = data.materialOverrides && Object.keys(data.materialOverrides).length > 0;
|
||||
const hasClipRect = !!data.clipRect;
|
||||
|
||||
const sprites: SpriteRenderData[] = [];
|
||||
for (let i = 0; i < data.tileCount; i++) {
|
||||
@@ -836,6 +910,11 @@ export class EngineRenderSystem extends EntitySystem {
|
||||
if (hasMaterialOverrides) {
|
||||
renderData.materialOverrides = data.materialOverrides;
|
||||
}
|
||||
// Add clipRect if present (all sprites in batch share same clipRect)
|
||||
// 如果存在 clipRect,添加它(批次中所有精灵共享相同 clipRect)
|
||||
if (hasClipRect) {
|
||||
renderData.clipRect = data.clipRect;
|
||||
}
|
||||
|
||||
sprites.push(renderData);
|
||||
}
|
||||
|
||||
@@ -52,6 +52,13 @@ export interface SpriteRenderData {
|
||||
* 材质属性覆盖(实例级别)。
|
||||
*/
|
||||
materialOverrides?: MaterialOverrides;
|
||||
/**
|
||||
* Clip rectangle for scissor test (screen coordinates).
|
||||
* Content outside this rect will be clipped.
|
||||
* 裁剪矩形用于 scissor test(屏幕坐标)。
|
||||
* 此矩形外的内容将被裁剪。
|
||||
*/
|
||||
clipRect?: { x: number; y: number; width: number; height: number };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -217,6 +217,20 @@ export class GameEngine {
|
||||
* * `id` - Texture ID | 纹理ID
|
||||
*/
|
||||
isTextureReady(id: number): boolean;
|
||||
/**
|
||||
* Set scissor rect for clipping (screen coordinates, Y-down).
|
||||
* 设置裁剪矩形(屏幕坐标,Y 轴向下)。
|
||||
*
|
||||
* Content outside this rect will be clipped.
|
||||
* 此矩形外的内容将被裁剪。
|
||||
*
|
||||
* # Arguments | 参数
|
||||
* * `x` - Left edge in screen coordinates | 屏幕坐标中的左边缘
|
||||
* * `y` - Top edge in screen coordinates (Y-down) | 屏幕坐标中的上边缘(Y 向下)
|
||||
* * `width` - Rect width | 矩形宽度
|
||||
* * `height` - Rect height | 矩形高度
|
||||
*/
|
||||
setScissorRect(x: number, y: number, width: number, height: number): void;
|
||||
/**
|
||||
* Add a capsule gizmo outline.
|
||||
* 添加胶囊Gizmo边框。
|
||||
@@ -269,6 +283,11 @@ export class GameEngine {
|
||||
* 请谨慎使用,因为所有纹理引用都将变得无效。
|
||||
*/
|
||||
clearAllTextures(): void;
|
||||
/**
|
||||
* Clear scissor rect (disable clipping).
|
||||
* 清除裁剪矩形(禁用裁剪)。
|
||||
*/
|
||||
clearScissorRect(): void;
|
||||
/**
|
||||
* Render to a specific viewport.
|
||||
* 渲染到特定视口。
|
||||
@@ -489,6 +508,7 @@ export interface InitOutput {
|
||||
readonly gameengine_addGizmoRect: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number) => void;
|
||||
readonly gameengine_clear: (a: number, b: number, c: number, d: number, e: number) => void;
|
||||
readonly gameengine_clearAllTextures: (a: number) => void;
|
||||
readonly gameengine_clearScissorRect: (a: number) => void;
|
||||
readonly gameengine_clearTexturePathCache: (a: number) => void;
|
||||
readonly gameengine_compileShader: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
|
||||
readonly gameengine_compileShaderWithId: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number];
|
||||
@@ -532,6 +552,7 @@ export interface InitOutput {
|
||||
readonly gameengine_setMaterialVec2: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
|
||||
readonly gameengine_setMaterialVec3: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number;
|
||||
readonly gameengine_setMaterialVec4: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => number;
|
||||
readonly gameengine_setScissorRect: (a: number, b: number, c: number, d: number, e: number) => void;
|
||||
readonly gameengine_setShowGizmos: (a: number, b: number) => void;
|
||||
readonly gameengine_setShowGrid: (a: number, b: number) => void;
|
||||
readonly gameengine_setTransformMode: (a: number, b: number) => void;
|
||||
|
||||
Reference in New Issue
Block a user