diff --git a/source/Component/CommandBar/CommandBar.tsx b/source/Component/CommandBar/CommandBar.tsx index bd6c5eb..ff4d5f7 100644 --- a/source/Component/CommandBar/CommandBar.tsx +++ b/source/Component/CommandBar/CommandBar.tsx @@ -59,8 +59,16 @@ class CommandBar extends Component this.props.status ? this.props.status.setMouseMod(MouseMod.click) : undefined })} - {this.getRenderButton({ iconName: "WebAppBuilderFragmentCreate", i18NKey: "Command.Bar.Add.Group.Info" })} - {this.getRenderButton({ iconName: "CubeShape", i18NKey: "Command.Bar.Add.Range.Info" })} + {this.getRenderButton({ + iconName: "WebAppBuilderFragmentCreate", + i18NKey: "Command.Bar.Add.Group.Info", + click: () => this.props.status ? this.props.status.newGroup() : undefined + })} + {this.getRenderButton({ + iconName: "CubeShape", + i18NKey: "Command.Bar.Add.Range.Info", + click: () => this.props.status ? this.props.status.newRange() : undefined + })} {this.getRenderButton({ iconName: "StepSharedAdd", i18NKey: "Command.Bar.Add.Behavior.Info" })} {this.getRenderButton({ iconName: "Tag", i18NKey: "Command.Bar.Add.Tag.Info" })} {this.getRenderButton({ iconName: "Camera", i18NKey: "Command.Bar.Camera.Info" })} diff --git a/source/Component/DetailsList/DetailsList.scss b/source/Component/DetailsList/DetailsList.scss new file mode 100644 index 0000000..011913f --- /dev/null +++ b/source/Component/DetailsList/DetailsList.scss @@ -0,0 +1,49 @@ +@import "../Theme/Theme.scss"; + +div.details-list { + width: 100%; + + div.details-list-item { + display: flex; + align-items: stretch; + user-select: none; + cursor: pointer; + min-height: 30px; + + div.details-list-value { + padding: 5px 10px; + display: flex; + justify-content: center; + align-items: center; + } + + div.details-list-checkbox { + display: flex; + align-items: center; + justify-content: center; + padding: 0 10px; + } + } +} + +div.light.details-list { + + div.details-list-item:nth-child(2n) { + background-color: rgba($lt-bg-color-lvl5-light, .4); + } + + div.details-list-item:hover { + background-color: $lt-bg-color-lvl3-light; + } +} + +div.dark.details-list { + + div.details-list-item:nth-child(2n) { + background-color: rgba($lt-bg-color-lvl5-dark, .4); + } + + div.details-list-item:hover { + background-color: $lt-bg-color-lvl3-dark; + } +} \ No newline at end of file diff --git a/source/Component/DetailsList/DetailsList.tsx b/source/Component/DetailsList/DetailsList.tsx new file mode 100644 index 0000000..a40eb0d --- /dev/null +++ b/source/Component/DetailsList/DetailsList.tsx @@ -0,0 +1,79 @@ +import { Icon } from "@fluentui/react"; +import { Component, ReactNode } from "react"; +import { BackgroundLevel, FontLevel, Theme } from "../Theme/Theme"; +import "./DetailsList.scss"; + +type IItems = Record & {key: string, select?: boolean}; + +interface IColumns { + key: K; + className?: string; + noDefaultStyle?: boolean; + beforeCheckbox?: boolean; + render: (data: D[K]) => ReactNode, + click?: (data: D[K]) => any, +} + +interface IDetailsListProps { + items: IItems[]; + columns: IColumns[]; + hideCheckBox?: boolean; + checkboxClassName?: string; +} + +class DetailsList extends Component { + + private renderValue(item: IItems, column: IColumns) { + const classList: string[] = []; + if (!column.noDefaultStyle) { + classList.push("details-list-value"); + } + if (column.className) { + classList.push(column.className); + } + return
+ {column.render(item[column.key as any])} +
+ } + + public render(): ReactNode { + return { + this.props.items.map((item) => { + const { checkboxClassName } = this.props; + return
+ { + this.props.columns.map((column) => { + if (column.beforeCheckbox) { + return this.renderValue(item, column); + } + }) + } + { + this.props.hideCheckBox ? null : +
+ +
+ } + { + this.props.columns.map((column) => { + if (!column.beforeCheckbox) { + return this.renderValue(item, column); + } + }) + } +
+ }) + }
+ } +} + +export { DetailsList }; \ No newline at end of file diff --git a/source/Component/Theme/Theme.scss b/source/Component/Theme/Theme.scss index ce09e98..3f24727 100644 --- a/source/Component/Theme/Theme.scss +++ b/source/Component/Theme/Theme.scss @@ -1,6 +1,6 @@ @import "@fluentui/react/dist/sass/References"; -$lt-font-size-normal: $ms-font-size-14; +$lt-font-size-normal: 13px; $lt-font-size-lvl3: $ms-font-size-16; $lt-font-size-lvl2: $ms-font-size-18; $lt-font-size-lvl1: $ms-font-size-24; diff --git a/source/Context/Status.tsx b/source/Context/Status.tsx index 90d0ead..0920a22 100644 --- a/source/Context/Status.tsx +++ b/source/Context/Status.tsx @@ -3,12 +3,29 @@ import { Emitter } from "@Model/Emitter"; import { Model } from "@Model/Model"; import { Archive } from "@Model/Archive"; import { AbstractRenderer } from "@Model/Renderer"; -import ClassicRenderer, { MouseMod } from "@GLRender/ClassicRenderer"; +import { ClassicRenderer, MouseMod } from "@GLRender/ClassicRenderer"; +import { Setting } from "./Setting"; +import { I18N } from "@Component/Localization/Localization"; + +function randomColor() { + return [ + Math.random() * .8 + .2, + Math.random() * .8 + .2, + Math.random() * .8 + .2, 1 + ] +} class Status extends Emitter<{ mouseModChange: MouseMod }> { + public setting: Setting = undefined as any; + + /** + * 对象命名 + */ + public objectNameIndex = 1; + /** * 渲染器 */ @@ -29,6 +46,26 @@ class Status extends Emitter<{ */ public mouseMod: MouseMod = MouseMod.Drag; + public newGroup() { + const group = this.model.addGroup(); + group.color = randomColor(); + group.displayName = I18N(this.setting.language, "Object.List.New.Group", { + id: this.objectNameIndex.toString() + }); + this.objectNameIndex ++; + return group; + } + + public newRange() { + const range = this.model.addRange(); + range.color = randomColor(); + range.displayName = I18N(this.setting.language, "Object.List.New.Range", { + id: this.objectNameIndex.toString() + }); + this.objectNameIndex ++; + return range; + } + public setMouseMod(mod: MouseMod) { this.mouseMod = mod; if (this.renderer instanceof ClassicRenderer) { diff --git a/source/Localization/EN-US.ts b/source/Localization/EN-US.ts index 608e97d..1ec0799 100644 --- a/source/Localization/EN-US.ts +++ b/source/Localization/EN-US.ts @@ -19,10 +19,15 @@ const EN_US = { "Command.Bar.Add.Tag.Info": "Add label object", "Command.Bar.Camera.Info": "Renderer settings", "Command.Bar.Setting.Info": "Global Settings", + "Object.List.New.Group": "Group object {id}", + "Object.List.New.Range": "Range object {id}", + "Object.List.No.Data": "There are no objects in the model, click the button to create it", "Panel.Title.Notfound": "{id}", "Panel.Info.Notfound": "This panel with id {id} can not found!", "Panel.Title.Render.View": "Live preview", "Panel.Info.Render.View": "Live simulation results preview", + "Panel.Title.Object.List.View": "Object list", + "Panel.Info.Object.List.View": "Edit View All Object Properties", } export default EN_US; \ No newline at end of file diff --git a/source/Localization/ZH-CN.ts b/source/Localization/ZH-CN.ts index aaa6290..c9ef69d 100644 --- a/source/Localization/ZH-CN.ts +++ b/source/Localization/ZH-CN.ts @@ -19,9 +19,14 @@ const ZH_CN = { "Command.Bar.Add.Tag.Info": "添加标签对象", "Command.Bar.Camera.Info": "渲染器设置", "Command.Bar.Setting.Info": "全局设置", + "Object.List.New.Group": "组对象 {id}", + "Object.List.New.Range": "范围对象 {id}", + "Object.List.No.Data": "模型中没有任何对象,点击按钮以创建", "Panel.Title.Notfound": "找不到面板: {id}", "Panel.Info.Notfound": "这个编号为 {id} 的面板无法找到!", "Panel.Title.Render.View": "实时预览", "Panel.Info.Render.View": "实时仿真结果预览", + "Panel.Title.Object.List.View": "对象列表", + "Panel.Info.Object.List.View": "编辑查看全部对象属性", } export default ZH_CN; \ No newline at end of file diff --git a/source/Model/CtrlObject.ts b/source/Model/CtrlObject.ts index 638cf7e..47857dc 100644 --- a/source/Model/CtrlObject.ts +++ b/source/Model/CtrlObject.ts @@ -7,6 +7,11 @@ import type { ObjectID } from "./Renderer"; */ class CtrlObject extends LabelObject { + /** + * 显示名称 + */ + public displayName: string = ""; + /** * 颜色 */ diff --git a/source/Page/SimulatorWeb/SimulatorWeb.tsx b/source/Page/SimulatorWeb/SimulatorWeb.tsx index cee10f3..72fae27 100644 --- a/source/Page/SimulatorWeb/SimulatorWeb.tsx +++ b/source/Page/SimulatorWeb/SimulatorWeb.tsx @@ -36,11 +36,12 @@ class SimulatorWeb extends Component { this.status = new Status(); this.status.renderer = new ClassicRenderer({ className: "canvas" }).onLoad(); this.status.model.bindRenderer(this.status.renderer); + this.status.setting = this.setting; // 测试代码 if (true) { - let group = this.status.model.addGroup(); - let range = this.status.model.addRange(); + let group = this.status.newGroup(); + let range = this.status.newRange(); range.color = [.1, .5, .9]; group.new(100); group.color = [.8, .1, .6]; @@ -72,7 +73,7 @@ class SimulatorWeb extends Component { }, { items: [{ - panles: ["Label d"] + panles: ["ObjectList"] }, { items: [{panles: ["Label e", "ee"]}, {panles: ["F"]}], layout: LayoutDirection.Y diff --git a/source/Panel/ObjectList/ObjectList.scss b/source/Panel/ObjectList/ObjectList.scss new file mode 100644 index 0000000..9879ba3 --- /dev/null +++ b/source/Panel/ObjectList/ObjectList.scss @@ -0,0 +1,7 @@ +div.object-list-color-value { + height: calc( 100% - 6px); + margin: 3px 0; + margin-left: 3px; + border-radius: 1000px; + width: 3px; +} \ No newline at end of file diff --git a/source/Panel/ObjectList/ObjectList.tsx b/source/Panel/ObjectList/ObjectList.tsx new file mode 100644 index 0000000..aa0bd8b --- /dev/null +++ b/source/Panel/ObjectList/ObjectList.tsx @@ -0,0 +1,76 @@ +import { Component, ReactNode } from "react"; +import { DetailsList } from "@Component/DetailsList/DetailsList"; +import { useStatus, IMixinStatusProps } from "@Context/Status"; +import { Localization } from "@Component/Localization/Localization"; +import "./ObjectList.scss"; + +@useStatus +class ObjectList extends Component { + + private handelChange = () => { + this.forceUpdate(); + } + + public componentDidMount(){ + if (this.props.status) { + this.props.status.model.on("objectChange", this.handelChange); + } + } + + public componentWillUnmount(){ + if (this.props.status) { + this.props.status.model.off("objectChange", this.handelChange); + } + } + + private renderList() { + const objList = this.props.status?.model.objectPool ?? []; + + if (objList.length <= 0) { + return + } + + return { + return { + key: object.id.toString(), + name: object.displayName, + color: object.color, + display: object.display, + update: object.update + } + }))} + columns={[ + { + key: "color", + noDefaultStyle: true, + beforeCheckbox: true, + render: (color) =>
+ }, { + key: "name", + render: (name) => {name} + } + ]} + /> + } + + public render(): ReactNode { + return this.renderList(); + } +} + +export { ObjectList }; \ No newline at end of file diff --git a/source/Panel/Panel.tsx b/source/Panel/Panel.tsx index b9605f0..7bda3a7 100644 --- a/source/Panel/Panel.tsx +++ b/source/Panel/Panel.tsx @@ -2,6 +2,7 @@ import { ReactNode, Component, FunctionComponent } from "react"; import { Theme } from "@Component/Theme/Theme"; import { Localization } from "@Component/Localization/Localization"; import { RenderView } from "./RenderView/RenderView"; +import { ObjectList } from "./ObjectList/ObjectList"; interface IPanelInfo { nameKey: string; @@ -15,6 +16,7 @@ interface IPanelInfo { type PanelId = "" | "RenderView" // 主渲染器 +| "ObjectList" // 对象列表 ; const PanelInfoMap = new Map(); @@ -22,6 +24,10 @@ PanelInfoMap.set("RenderView", { nameKey: "Panel.Title.Render.View", introKay: "Panel.Info.Render.View", class: RenderView, hidePadding: true, hideScrollBar: true, isDeepDark: true }); +PanelInfoMap.set("ObjectList", { + nameKey: "Panel.Title.Object.List.View", introKay: "Panel.Info.Object.List.View", + class: ObjectList, hidePadding: true +}) function getPanelById(panelId: PanelId): ReactNode { switch (panelId) {