Compare commits
	
		
			No commits in common. "b6e13fa29694b24573d91317ba7f930789fa6f65" and "bb762c827367d5a955a3d801037c12c1b2b4d049" have entirely different histories.
		
	
	
		
			b6e13fa296
			...
			bb762c8273
		
	
		
| @ -1,8 +0,0 @@ | |||||||
| import { BehaviorRecorder, IAnyBehaviorRecorder } from "@Model/Behavior"; |  | ||||||
| import { Template } from "./Template"; |  | ||||||
| 
 |  | ||||||
| const AllBehaviors: IAnyBehaviorRecorder[] = [ |  | ||||||
|     new BehaviorRecorder(Template) |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| export { AllBehaviors }; |  | ||||||
| @ -1,20 +0,0 @@ | |||||||
| import { Behavior } from "@Model/Behavior"; |  | ||||||
| 
 |  | ||||||
| type ITemplateBehaviorParameter = { |  | ||||||
|      |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type ITemplateBehaviorEvent = {} |  | ||||||
| 
 |  | ||||||
| class Template extends Behavior<ITemplateBehaviorParameter, ITemplateBehaviorEvent> { |  | ||||||
| 
 |  | ||||||
|     public override behaviorId: string = "Template"; |  | ||||||
| 
 |  | ||||||
|     public override behaviorName: string = "Behavior.Template.Title"; |  | ||||||
| 
 |  | ||||||
|     public override iconName: string = "Running"; |  | ||||||
| 
 |  | ||||||
|     public override describe: string = "Behavior.Template.Intro"; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export { Template }; |  | ||||||
| @ -1,11 +0,0 @@ | |||||||
| @import "../Theme/Theme.scss"; |  | ||||||
| 
 |  | ||||||
| div.behavior-popup { |  | ||||||
| 	width: 100%; |  | ||||||
| 	height: 100%; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| div.behavior-popup-search-box { |  | ||||||
| 	padding: 10px 0 0 10px; |  | ||||||
| 	width: calc(100% - 10px); |  | ||||||
| } |  | ||||||
| @ -1,65 +0,0 @@ | |||||||
| import { Component, ReactNode } from "react"; |  | ||||||
| import { Popup } from "@Context/Popups"; |  | ||||||
| import { Localization } from "@Component/Localization/Localization"; |  | ||||||
| import { SearchBox } from "@Component/SearchBox/SearchBox"; |  | ||||||
| import { ConfirmContent } from "@Component/ConfirmPopup/ConfirmPopup"; |  | ||||||
| import "./BehaviorPopup.scss"; |  | ||||||
| 
 |  | ||||||
| interface IBehaviorPopupProps { |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| interface IBehaviorPopupState { |  | ||||||
| 	searchValue: string; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class BehaviorPopup extends Popup<IBehaviorPopupProps> { |  | ||||||
| 
 |  | ||||||
| 	public minWidth: number = 400; |  | ||||||
| 	public minHeight: number = 300; |  | ||||||
| 	public width: number = 600; |  | ||||||
| 	public height: number = 450; |  | ||||||
| 
 |  | ||||||
| 	public onRenderHeader(): ReactNode { |  | ||||||
| 		return <Localization i18nKey="Popup.Add.Behavior.Title"/> |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public render(): ReactNode { |  | ||||||
| 		return <BehaviorPopupComponent {...this.props}/> |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class BehaviorPopupComponent extends Component<IBehaviorPopupProps, IBehaviorPopupState> { |  | ||||||
| 
 |  | ||||||
| 	state: Readonly<IBehaviorPopupState> = { |  | ||||||
| 		searchValue: "" |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	private renderHeader = () => { |  | ||||||
| 		return <div className="behavior-popup-search-box"> |  | ||||||
| 			<SearchBox |  | ||||||
| 				valueChange={(value) => { |  | ||||||
| 					this.setState({ |  | ||||||
| 						searchValue: value |  | ||||||
| 					}); |  | ||||||
| 				}} |  | ||||||
| 				value={this.state.searchValue} |  | ||||||
| 			/> |  | ||||||
| 		</div>; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public render(): ReactNode { |  | ||||||
| 		return <ConfirmContent |  | ||||||
| 			className="behavior-popup" |  | ||||||
| 			actions={[{ |  | ||||||
| 				i18nKey: "Popup.Add.Behavior.Action.Add" |  | ||||||
| 			}]} |  | ||||||
| 			header={this.renderHeader} |  | ||||||
| 			headerHeight={36} |  | ||||||
| 		> |  | ||||||
| 			 |  | ||||||
| 		</ConfirmContent> |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export { BehaviorPopup }; |  | ||||||
| @ -4,8 +4,7 @@ import { LocalizationTooltipHost } from "../Localization/LocalizationTooltipHost | |||||||
| import { useSetting, IMixinSettingProps } from "@Context/Setting"; | import { useSetting, IMixinSettingProps } from "@Context/Setting"; | ||||||
| import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; | import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; | ||||||
| import { AllI18nKeys } from "../Localization/Localization"; | import { AllI18nKeys } from "../Localization/Localization"; | ||||||
| import { SettingPopup } from "../SettingPopup/SettingPopup"; | import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; | ||||||
| import { BehaviorPopup } from "../BehaviorPopup/BehaviorPopup"; |  | ||||||
| import { Component, ReactNode } from "react"; | import { Component, ReactNode } from "react"; | ||||||
| import { MouseMod } from "@GLRender/ClassicRenderer"; | import { MouseMod } from "@GLRender/ClassicRenderer"; | ||||||
| import "./CommandBar.scss"; | import "./CommandBar.scss"; | ||||||
| @ -53,19 +52,13 @@ class CommandBar extends Component<ICommandBarProps & IMixinSettingProps & IMixi | |||||||
|                     } |                     } | ||||||
|                 })} |                 })} | ||||||
|                 {this.getRenderButton({ |                 {this.getRenderButton({ | ||||||
|                     iconName: "ProductVariant", |                     iconName: "CubeShape", | ||||||
|                     i18NKey: "Command.Bar.Add.Range.Info", |                     i18NKey: "Command.Bar.Add.Range.Info", | ||||||
|                     click: () => { |                     click: () => { | ||||||
|                         this.props.status ? this.props.status.newRange() : undefined; |                         this.props.status ? this.props.status.newRange() : undefined; | ||||||
|                     } |                     } | ||||||
|                 })} |                 })} | ||||||
|                 {this.getRenderButton({ |                 {this.getRenderButton({ iconName: "StepSharedAdd", i18NKey: "Command.Bar.Add.Behavior.Info" })} | ||||||
|                     iconName: "Running", |  | ||||||
|                     i18NKey: "Command.Bar.Add.Behavior.Info", |  | ||||||
|                     click: () => { |  | ||||||
|                         this.props.status?.popup.showPopup(BehaviorPopup, {}); |  | ||||||
|                     } |  | ||||||
|                 })} |  | ||||||
|                 {this.getRenderButton({ |                 {this.getRenderButton({ | ||||||
|                     iconName: "Tag", |                     iconName: "Tag", | ||||||
|                     i18NKey: "Command.Bar.Add.Tag.Info", |                     i18NKey: "Command.Bar.Add.Tag.Info", | ||||||
| @ -80,7 +73,7 @@ class CommandBar extends Component<ICommandBarProps & IMixinSettingProps & IMixi | |||||||
|                     iconName: "Settings", |                     iconName: "Settings", | ||||||
|                     i18NKey: "Command.Bar.Setting.Info", |                     i18NKey: "Command.Bar.Setting.Info", | ||||||
|                     click: () => { |                     click: () => { | ||||||
|                         this.props.status?.popup.showPopup(SettingPopup, {}); |                         // this.props.status?.popup.showPopup(ConfirmPopup, {});
 | ||||||
|                     } |                     } | ||||||
|                 })} |                 })} | ||||||
|             </div> |             </div> | ||||||
|  | |||||||
| @ -4,35 +4,10 @@ div.confirm-root { | |||||||
| 	width: 100%; | 	width: 100%; | ||||||
| 	height: 100%; | 	height: 100%; | ||||||
| 
 | 
 | ||||||
| 	div.header-view { |  | ||||||
| 		width: 100%; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	div.content-views { | 	div.content-views { | ||||||
| 		width: 100%; | 		width: 100%; | ||||||
|  | 		height: calc( 100% - 36px ); | ||||||
| 		box-sizing: border-box; | 		box-sizing: border-box; | ||||||
| 		overflow: scroll; |  | ||||||
| 		-ms-overflow-style: none; |  | ||||||
| 		flex-shrink: 1; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	div.content-views::-webkit-scrollbar { |  | ||||||
| 		width : 8px;  /*高宽分别对应横竖滚动条的尺寸*/ |  | ||||||
|   		height: 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	div.content-views::-webkit-scrollbar-thumb { |  | ||||||
| 		/*滚动条里面小方块*/ |  | ||||||
| 		border-radius: 8px; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	div.content-views::-webkit-scrollbar-track { |  | ||||||
| 		/*滚动条里面轨道*/ |  | ||||||
| 		border-radius: 8px; |  | ||||||
| 		background-color: rgba($color: #000000, $alpha: 0); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	div.content-views.has-padding { |  | ||||||
| 		padding: 10px; | 		padding: 10px; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -60,27 +35,14 @@ div.confirm-root { | |||||||
| 		div.action-button.red { | 		div.action-button.red { | ||||||
| 			color: $lt-red; | 			color: $lt-red; | ||||||
| 		} | 		} | ||||||
| 
 |  | ||||||
| 		div.action-button.blue { |  | ||||||
| 			color: $lt-blue; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		div.action-button.disable { |  | ||||||
| 			opacity: .75; |  | ||||||
| 			cursor: not-allowed; |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| div.dark.confirm-root { | div.dark.confirm-root { | ||||||
| 
 | 
 | ||||||
| 	div.content-views::-webkit-scrollbar-thumb { |  | ||||||
| 		background-color: $lt-bg-color-lvl1-dark; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	div.action-view { | 	div.action-view { | ||||||
| 
 | 
 | ||||||
| 		div.action-button, div.action-button.disable:hover { | 		div.action-button { | ||||||
| 			background-color: $lt-bg-color-lvl3-dark; | 			background-color: $lt-bg-color-lvl3-dark; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| @ -92,13 +54,9 @@ div.dark.confirm-root { | |||||||
| 
 | 
 | ||||||
| div.light.confirm-root { | div.light.confirm-root { | ||||||
| 
 | 
 | ||||||
| 	div.content-views::-webkit-scrollbar-thumb { |  | ||||||
| 		background-color: $lt-bg-color-lvl1-light; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	div.action-view { | 	div.action-view { | ||||||
| 
 | 
 | ||||||
| 		div.action-button, div.action-button.disable:hover { | 		div.action-button { | ||||||
| 			background-color: $lt-bg-color-lvl3-light; | 			background-color: $lt-bg-color-lvl3-light; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import { Popup } from "@Context/Popups"; | import { Popup } from "@Context/Popups"; | ||||||
| import { Component, ReactNode } from "react"; | import { ReactNode } from "react"; | ||||||
| import { Message } from "@Component/Message/Message"; | import { Message } from "@Component/Message/Message"; | ||||||
| import { Theme } from "@Component/Theme/Theme"; | import { Theme } from "@Component/Theme/Theme"; | ||||||
| import { AllI18nKeys, Localization } from "@Component/Localization/Localization"; | import { AllI18nKeys, Localization } from "@Component/Localization/Localization"; | ||||||
| @ -7,12 +7,12 @@ import "./ConfirmPopup.scss"; | |||||||
| 
 | 
 | ||||||
| interface IConfirmPopupProps { | interface IConfirmPopupProps { | ||||||
| 	titleI18N?: AllI18nKeys; | 	titleI18N?: AllI18nKeys; | ||||||
| 	infoI18n?: AllI18nKeys; | 	infoI18n: AllI18nKeys; | ||||||
| 	yesI18n?: AllI18nKeys; | 	yesI18n?: AllI18nKeys; | ||||||
| 	noI18n?: AllI18nKeys; | 	noI18n?: AllI18nKeys; | ||||||
| 	red?: "yes" | "no"; |  | ||||||
| 	yes?: () => any; | 	yes?: () => any; | ||||||
| 	no?: () => any; | 	no?: () => any; | ||||||
|  | 	red?: "yes" | "no"; | ||||||
| } | } | ||||||
| class ConfirmPopup extends Popup<IConfirmPopupProps> { | class ConfirmPopup extends Popup<IConfirmPopupProps> { | ||||||
| 
 | 
 | ||||||
| @ -24,139 +24,37 @@ class ConfirmPopup extends Popup<IConfirmPopupProps> { | |||||||
| 		return <Localization i18nKey={this.props.titleI18N ?? "Popup.Title.Confirm"}/> | 		return <Localization i18nKey={this.props.titleI18N ?? "Popup.Title.Confirm"}/> | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private genActionClickFunction(fn?: () => any): () => any { |  | ||||||
| 		return () => { |  | ||||||
| 			if (fn) fn(); |  | ||||||
| 			this.close(); |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public render(): ReactNode { | 	public render(): ReactNode { | ||||||
| 
 | 
 | ||||||
| 		const actionList: IActionButtonProps[] = []; | 		const yesClassList: string[] = ["action-button", "yes-button"]; | ||||||
| 
 | 		const noClassList: string[] = ["action-button", "no-button"]; | ||||||
| 		if (this.props.yesI18n || this.props.yes) { | 		if (this.props.red === "no") { | ||||||
| 			actionList.push({ | 			noClassList.push("red"); | ||||||
| 				i18nKey: this.props.yesI18n ?? "Popup.Action.Yes", |  | ||||||
| 				onClick: this.genActionClickFunction(this.props.yes), |  | ||||||
| 				color: this.props.red === "yes" ? "red" : undefined |  | ||||||
| 			}); |  | ||||||
| 		} | 		} | ||||||
| 
 | 		if (this.props.red === "yes") { | ||||||
| 		if (this.props.noI18n || this.props.no) { | 			yesClassList.push("red"); | ||||||
| 			actionList.push({ |  | ||||||
| 				i18nKey: this.props.noI18n ?? "Popup.Action.Yes", |  | ||||||
| 				onClick: this.genActionClickFunction(this.props.no), |  | ||||||
| 				color: this.props.red === "no" ? "red" : undefined |  | ||||||
| 			}); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		return <ConfirmContent |  | ||||||
| 			actions={actionList} |  | ||||||
| 		> |  | ||||||
| 			{this.props.infoI18n ? <Message i18nKey={this.props.infoI18n}/> : null} |  | ||||||
| 		</ConfirmContent> |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| interface IConfirmContentProps { |  | ||||||
| 	hidePadding?: boolean; |  | ||||||
| 	className?: string; |  | ||||||
| 	actions: IActionButtonProps[]; |  | ||||||
| 	header?: () => ReactNode; |  | ||||||
| 	headerHeight?: number; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| interface IActionButtonProps { |  | ||||||
| 	className?: string; |  | ||||||
| 	disable?: boolean; |  | ||||||
| 	color?: "red" | "blue"; |  | ||||||
| 	i18nKey: AllI18nKeys; |  | ||||||
| 	i18nOption?: Record<string, string>; |  | ||||||
| 	onClick?: () => void; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class ConfirmContent extends Component<IConfirmContentProps> { |  | ||||||
| 
 |  | ||||||
| 	public renderActionButton(props: IActionButtonProps, key: number): ReactNode { |  | ||||||
| 
 |  | ||||||
| 		const classList = ["action-button"]; |  | ||||||
| 		if (props.className) { |  | ||||||
| 			classList.push(props.className); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (props.color === "red") { |  | ||||||
| 			classList.push("red"); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (props.color === "blue") { |  | ||||||
| 			classList.push("blue"); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (props.disable) { |  | ||||||
| 			classList.push("disable"); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		return <div |  | ||||||
| 			className={classList.join(" ")} |  | ||||||
| 			onClick={props.disable ? undefined : props.onClick} |  | ||||||
| 			key={key} |  | ||||||
| 		> |  | ||||||
| 			<Localization i18nKey={props.i18nKey} options={props.i18nOption}/> |  | ||||||
| 		</div> |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	private getHeaderHeight(): number { |  | ||||||
| 		return this.props.headerHeight ?? 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	private renderHeader() { |  | ||||||
| 		return <div |  | ||||||
| 			className="header-view" |  | ||||||
| 			style={{ |  | ||||||
| 				maxHeight: this.getHeaderHeight(), |  | ||||||
| 				minHeight: this.getHeaderHeight(), |  | ||||||
| 				height: this.getHeaderHeight() |  | ||||||
| 			}} |  | ||||||
| 		> |  | ||||||
| 			{this.props.header ? this.props.header() : null} |  | ||||||
| 		</div> |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public render(): ReactNode { |  | ||||||
| 
 |  | ||||||
| 		const contentClassNameList: string[] = ["content-views"]; |  | ||||||
| 
 |  | ||||||
| 		if (this.props.className) { |  | ||||||
| 			contentClassNameList.push(this.props.className); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (!this.props.hidePadding) { |  | ||||||
| 			contentClassNameList.push("has-padding"); |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return <Theme className="confirm-root"> | 		return <Theme className="confirm-root"> | ||||||
| 
 | 			<div className="content-views"> | ||||||
| 			{this.props.header ? this.renderHeader() : null} | 				<Message i18nKey={this.props.infoI18n}/> | ||||||
| 
 |  | ||||||
| 			<div |  | ||||||
| 				className={contentClassNameList.join(" ")} |  | ||||||
| 				style={{ |  | ||||||
| 					height: `calc( 100% - ${this.getHeaderHeight() + 36}px )` |  | ||||||
| 				}} |  | ||||||
| 			> |  | ||||||
| 				{this.props.children} |  | ||||||
| 			</div> | 			</div> | ||||||
| 			 |  | ||||||
| 			<div className="action-view"> | 			<div className="action-view"> | ||||||
| 				{ | 				<div className={yesClassList.join(" ")} onClick={() => { | ||||||
| 					this.props.actions.map((prop, index) => { | 					this.props.yes ? this.props.yes() : null; | ||||||
| 						return this.renderActionButton(prop, index); | 					this.close(); | ||||||
| 					}) | 				}}> | ||||||
| 				} | 					<Localization i18nKey={this.props.yesI18n ?? "Popup.Action.Yes"}/> | ||||||
|  | 				</div> | ||||||
|  | 				<div className={noClassList.join(" ")} onClick={() => { | ||||||
|  | 					this.props.no ? this.props.no() : null; | ||||||
|  | 					this.close(); | ||||||
|  | 				}}> | ||||||
|  | 					<Localization i18nKey={this.props.noI18n ?? "Popup.Action.No"}/> | ||||||
|  | 				</div> | ||||||
| 			</div> | 			</div> | ||||||
| 		</Theme>; | 		</Theme>; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export { ConfirmPopup, ConfirmContent } | export { ConfirmPopup } | ||||||
| @ -1,95 +0,0 @@ | |||||||
| @import "../Theme/Theme.scss"; |  | ||||||
| 
 |  | ||||||
| $search-box-height: 26px; |  | ||||||
| 
 |  | ||||||
| div.search-box-root { |  | ||||||
| 	min-height: $search-box-height; |  | ||||||
| 	max-width: 280px; |  | ||||||
| 	width: 100%; |  | ||||||
| 	border-radius: 3px; |  | ||||||
| 	display: flex; |  | ||||||
| 	cursor: pointer; |  | ||||||
| 	overflow: hidden; |  | ||||||
| 
 |  | ||||||
| 	div.search-icon { |  | ||||||
| 		min-width: $search-box-height; |  | ||||||
| 		height: $search-box-height; |  | ||||||
| 		flex-shrink: 0; |  | ||||||
| 		display: flex; |  | ||||||
| 		justify-content: center; |  | ||||||
| 		align-items: center; |  | ||||||
| 		user-select: none; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	div.input-box { |  | ||||||
| 		width: calc(100% - 26px); |  | ||||||
| 		height: $search-box-height; |  | ||||||
| 
 |  | ||||||
| 		input { |  | ||||||
| 			width: 100%; |  | ||||||
| 			height: 100%; |  | ||||||
| 			padding: 0; |  | ||||||
| 			margin: 0; |  | ||||||
| 			border: 0; |  | ||||||
| 			outline: none; |  | ||||||
| 			background-color: transparent; |  | ||||||
| 			vertical-align: middle; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	div.clean-box { |  | ||||||
| 		height: $search-box-height; |  | ||||||
| 		width: 0; |  | ||||||
| 		display: flex; |  | ||||||
| 		align-items: center; |  | ||||||
| 
 |  | ||||||
| 		div.clean-box-view { |  | ||||||
| 			flex-shrink: 0; |  | ||||||
| 			height: 24px; |  | ||||||
| 			width: 24px; |  | ||||||
| 			display: flex; |  | ||||||
| 			justify-content: center; |  | ||||||
| 			align-items: center; |  | ||||||
| 			position: relative; |  | ||||||
| 			right: 24px; |  | ||||||
| 			border-radius: 3px; |  | ||||||
| 			user-select: none; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| div.dark.search-box-root { |  | ||||||
| 
 |  | ||||||
| 	div.clean-box { |  | ||||||
| 
 |  | ||||||
| 		div.clean-box-view:hover { |  | ||||||
| 			background-color: $lt-bg-color-lvl2-dark; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		div.clean-box-view { |  | ||||||
| 			background-color: $lt-bg-color-lvl3-dark; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	div.input-box input { |  | ||||||
| 		color: $lt-font-color-normal-dark; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| div.light.search-box-root { |  | ||||||
| 
 |  | ||||||
| 	div.clean-box { |  | ||||||
| 
 |  | ||||||
| 		div.clean-box-view:hover { |  | ||||||
| 			background-color: $lt-bg-color-lvl3-light; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		div.clean-box-view { |  | ||||||
| 			background-color: $lt-bg-color-lvl2-light; |  | ||||||
| 		} |  | ||||||
| 	}  |  | ||||||
| 
 |  | ||||||
| 	div.input-box input { |  | ||||||
| 		color: $lt-font-color-normal-light; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,60 +0,0 @@ | |||||||
| import { AllI18nKeys, I18N } from "@Component/Localization/Localization"; |  | ||||||
| import { BackgroundLevel, FontLevel, Theme } from "@Component/Theme/Theme"; |  | ||||||
| import { useSettingWithEvent, IMixinSettingProps } from "@Context/Setting"; |  | ||||||
| import { Icon } from "@fluentui/react"; |  | ||||||
| import { Component, ReactNode } from "react"; |  | ||||||
| import "./SearchBox.scss"; |  | ||||||
| 
 |  | ||||||
| interface ISearchBoxProps { |  | ||||||
| 	value?: string; |  | ||||||
| 	valueChange?: (value: string) => void; |  | ||||||
| 	placeholderI18N?: AllI18nKeys; |  | ||||||
| 	className?: string; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @useSettingWithEvent("language") |  | ||||||
| class SearchBox extends Component<ISearchBoxProps & IMixinSettingProps> { |  | ||||||
| 
 |  | ||||||
| 	private renderCleanBox() { |  | ||||||
| 		return <div className="clean-box"> |  | ||||||
| 			<div |  | ||||||
| 				className="clean-box-view" |  | ||||||
| 				onClick={() => { |  | ||||||
| 					if (this.props.valueChange) { |  | ||||||
| 						this.props.valueChange("") |  | ||||||
| 					} |  | ||||||
| 				}} |  | ||||||
| 			> |  | ||||||
| 				<Icon iconName="CalculatorMultiply"/> |  | ||||||
| 			</div> |  | ||||||
| 		</div>; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public render(): ReactNode { |  | ||||||
| 		return <Theme |  | ||||||
| 			className={"search-box-root" + (this.props.className ? ` ${this.props.className}` : "")} |  | ||||||
| 			backgroundLevel={BackgroundLevel.Level3} |  | ||||||
| 			fontLevel={FontLevel.normal} |  | ||||||
| 		> |  | ||||||
| 			<div className="search-icon"> |  | ||||||
| 				<Icon iconName="search"/> |  | ||||||
| 			</div> |  | ||||||
| 			<div className="input-box"> |  | ||||||
| 				<input |  | ||||||
| 					value={this.props.value} |  | ||||||
| 					placeholder={ |  | ||||||
| 						I18N(this.props, this.props.placeholderI18N ?? "Common.Search.Placeholder") |  | ||||||
| 					} |  | ||||||
| 					onInput={(e) => { |  | ||||||
| 						if (e.target instanceof HTMLInputElement && this.props.valueChange) { |  | ||||||
| 							this.props.valueChange(e.target.value) |  | ||||||
| 						} |  | ||||||
| 					}} |  | ||||||
| 				/> |  | ||||||
| 			</div> |  | ||||||
| 			{this.props.value ? this.renderCleanBox() : null} |  | ||||||
| 		</Theme> |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export { SearchBox }; |  | ||||||
| @ -1,6 +0,0 @@ | |||||||
| @import "../Theme/Theme.scss"; |  | ||||||
| 
 |  | ||||||
| div.setting-popup { |  | ||||||
| 	width: 100%; |  | ||||||
| 	height: 100%; |  | ||||||
| } |  | ||||||
| @ -1,34 +0,0 @@ | |||||||
| import { Component, ReactNode } from "react"; |  | ||||||
| import { Popup } from "@Context/Popups"; |  | ||||||
| import { Theme } from "@Component/Theme/Theme"; |  | ||||||
| import { Localization } from "@Component/Localization/Localization"; |  | ||||||
| import "./SettingPopup.scss"; |  | ||||||
| 
 |  | ||||||
| interface ISettingPopupProps { |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class SettingPopup extends Popup<ISettingPopupProps> { |  | ||||||
| 
 |  | ||||||
| 	public minWidth: number = 400; |  | ||||||
| 	public minHeight: number = 300; |  | ||||||
| 	public width: number = 600; |  | ||||||
| 	public height: number = 450; |  | ||||||
| 
 |  | ||||||
| 	public onRenderHeader(): ReactNode { |  | ||||||
| 		return <Localization i18nKey="Popup.Setting.Title"/> |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public render(): ReactNode { |  | ||||||
| 		return <SettingPopupComponent {...this.props}/> |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class SettingPopupComponent extends Component<ISettingPopupProps> { |  | ||||||
| 
 |  | ||||||
| 	public render(): ReactNode { |  | ||||||
| 		return <Theme className="setting-popup"></Theme> |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export { SettingPopup }; |  | ||||||
| @ -83,13 +83,13 @@ class Status extends Emitter<IStatusEvent> { | |||||||
|      */ |      */ | ||||||
|     public focusLabel?: Label; |     public focusLabel?: Label; | ||||||
| 
 | 
 | ||||||
|     private drawTimer?: NodeJS.Timeout; |     private drawtimer?: NodeJS.Timeout; | ||||||
| 
 | 
 | ||||||
|     private delayDraw = () => { |     private delayDraw = () => { | ||||||
|         this.drawTimer ? clearTimeout(this.drawTimer) : null; |         this.drawtimer ? clearTimeout(this.drawtimer) : null; | ||||||
|         this.drawTimer = setTimeout(() => { |         this.drawtimer = setTimeout(() => { | ||||||
|             this.model.draw(); |             this.model.draw(); | ||||||
|             this.drawTimer = undefined; |             this.drawtimer = undefined; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -51,14 +51,8 @@ const EN_US = { | |||||||
|     "Popup.Action.Objects.Confirm.Title": "Confirm Delete", |     "Popup.Action.Objects.Confirm.Title": "Confirm Delete", | ||||||
|     "Popup.Action.Objects.Confirm.Delete": "Delete", |     "Popup.Action.Objects.Confirm.Delete": "Delete", | ||||||
|     "Popup.Delete.Objects.Confirm": "Are you sure you want to delete this object(s)? The object is deleted and cannot be recalled.", |     "Popup.Delete.Objects.Confirm": "Are you sure you want to delete this object(s)? The object is deleted and cannot be recalled.", | ||||||
|     "Popup.Setting.Title": "Preferences setting", |  | ||||||
|     "Popup.Add.Behavior.Title": "Add behavior", |  | ||||||
|     "Popup.Add.Behavior.Action.Add": "Add all select behavior", |  | ||||||
|     "Build.In.Label.Name.All.Group": "All group", |     "Build.In.Label.Name.All.Group": "All group", | ||||||
|     "Build.In.Label.Name.All.Range": "All range", |     "Build.In.Label.Name.All.Range": "All range", | ||||||
|     "Behavior.Template.Title": "Behavior", |  | ||||||
|     "Behavior.Template.Intro": "This is a template behavior", |  | ||||||
|     "Common.Search.Placeholder": "Search in here...", |  | ||||||
|     "Common.No.Data": "No Data", |     "Common.No.Data": "No Data", | ||||||
|     "Common.No.Unknown.Error": "Unknown error", |     "Common.No.Unknown.Error": "Unknown error", | ||||||
|     "Common.Attr.Title.Basic": "Basic properties", |     "Common.Attr.Title.Basic": "Basic properties", | ||||||
|  | |||||||
| @ -51,14 +51,8 @@ const ZH_CN = { | |||||||
|     "Popup.Action.Objects.Confirm.Title": "删除确认", |     "Popup.Action.Objects.Confirm.Title": "删除确认", | ||||||
|     "Popup.Action.Objects.Confirm.Delete": "删除", |     "Popup.Action.Objects.Confirm.Delete": "删除", | ||||||
|     "Popup.Delete.Objects.Confirm": "你确定要删除这个(些)对象吗?对象被删除将无法撤回。", |     "Popup.Delete.Objects.Confirm": "你确定要删除这个(些)对象吗?对象被删除将无法撤回。", | ||||||
|     "Popup.Setting.Title": "首选项设置", |  | ||||||
|     "Popup.Add.Behavior.Title": "添加行为", |  | ||||||
|     "Popup.Add.Behavior.Action.Add": "添加全部选中行为", |  | ||||||
|     "Build.In.Label.Name.All.Group": "全部群", |     "Build.In.Label.Name.All.Group": "全部群", | ||||||
|     "Build.In.Label.Name.All.Range": "全部范围", |     "Build.In.Label.Name.All.Range": "全部范围", | ||||||
|     "Behavior.Template.Title": "行为", |  | ||||||
|     "Behavior.Template.Intro": "这是一个模板行为", |  | ||||||
|     "Common.Search.Placeholder": "在此处搜索...", |  | ||||||
|     "Common.No.Data": "暂无数据", |     "Common.No.Data": "暂无数据", | ||||||
|     "Common.No.Unknown.Error": "未知错误", |     "Common.No.Unknown.Error": "未知错误", | ||||||
|     "Common.Attr.Title.Basic": "基础属性", |     "Common.Attr.Title.Basic": "基础属性", | ||||||
|  | |||||||
| @ -3,330 +3,40 @@ import { Emitter, EventType } from "./Emitter"; | |||||||
| import type { Individual } from "./Individual"; | import type { Individual } from "./Individual"; | ||||||
| import type { Group } from "./Group"; | import type { Group } from "./Group"; | ||||||
| import type { Model } from "./Model"; | import type { Model } from "./Model"; | ||||||
| import type { Range } from "./Range"; |  | ||||||
| import type { Label } from "./Label"; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 参数类型 |  * 群体的某种行为 | ||||||
|  */ |  */ | ||||||
| type IMapBasicParamTypeKeyToType = { | abstract class Behavior< | ||||||
|     "number": number; |     P extends IAnyObject = {}, | ||||||
|     "string": string; |  | ||||||
|     "boolean": boolean; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type IMapObjectParamTypeKeyToType = { |  | ||||||
|     "R"?: Range; |  | ||||||
|     "G"?: Group; |  | ||||||
|     "GR"?: Group | Range; |  | ||||||
|     "LR"?: Label | Range; |  | ||||||
|     "LG"?: Label | Group; |  | ||||||
|     "LGR"?: Label | Group | Range; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type IMapVectorParamTypeKeyToType = { |  | ||||||
|     "vec": number[]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 参数类型映射 |  | ||||||
|  */ |  | ||||||
| type AllMapType = IMapBasicParamTypeKeyToType & IMapObjectParamTypeKeyToType & IMapVectorParamTypeKeyToType; |  | ||||||
| type IParamType = keyof AllMapType; |  | ||||||
| type IObjectType = keyof IMapObjectParamTypeKeyToType; |  | ||||||
| type IVectorType = keyof IMapVectorParamTypeKeyToType; |  | ||||||
| type IParamValue<K extends IParamType> = AllMapType[K]; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 特殊对象类型判定 |  | ||||||
|  */ |  | ||||||
| const objectTypeListEnumSet = new Set<IParamType>(["R", "G", "GR", "LR", "LG", "LGR"]); |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 对象断言表达式 |  | ||||||
|  */ |  | ||||||
| function isObjectType(key: IParamType): key is IVectorType { |  | ||||||
|     return objectTypeListEnumSet.has(key); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 向量断言表达式 |  | ||||||
|  */ |  | ||||||
| function isVectorType(key: IParamType): key is IObjectType { |  | ||||||
|     return key === "vec"; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 模型参数类型 |  | ||||||
|  */ |  | ||||||
| interface IBehaviorParameterOptionItem<T extends IParamType = IParamType> { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 参数类型 |  | ||||||
|      */ |  | ||||||
|     type: T; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 参数默认值 |  | ||||||
|      */ |  | ||||||
|     defaultValue?: IParamValue<T>; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 数值变化回调 |  | ||||||
|      */ |  | ||||||
|     onChange?: (value: IParamValue<T>) => any; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 名字 |  | ||||||
|      */ |  | ||||||
|     name: string; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 字符长度 |  | ||||||
|      */ |  | ||||||
|     stringLength?: number; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 数字步长 |  | ||||||
|      */ |  | ||||||
|     numberStep?: number; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 最大值最小值 |  | ||||||
|      */ |  | ||||||
|     numberMax?: number; |  | ||||||
|     numberMin?: number; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 图标名字 |  | ||||||
|      */ |  | ||||||
|     iconName?: string; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| interface IBehaviorParameter { |  | ||||||
|     [x: string]: IParamType; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 参数类型列表 |  | ||||||
|  */ |  | ||||||
| type IBehaviorParameterOption<P extends IBehaviorParameter> = { |  | ||||||
|     [X in keyof P]: IBehaviorParameterOptionItem<P[X]>; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 参数类型列表映射到参数对象 |  | ||||||
|  */ |  | ||||||
| type IBehaviorParameterValue<P extends IBehaviorParameter> = { |  | ||||||
|     [X in keyof P]: IParamValue<P[X]> |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 行为构造函数类型 |  | ||||||
|  */ |  | ||||||
| type IBehaviorConstructor< |  | ||||||
|     P extends IBehaviorParameter = {}, |  | ||||||
|     E extends Record<EventType, any> = {} |     E extends Record<EventType, any> = {} | ||||||
| > = new (id: string, parameter: IBehaviorParameterValue<P>) => Behavior<P, E>; | > extends Emitter<E> { | ||||||
| 
 |  | ||||||
| type IAnyBehavior = Behavior<any, any>; |  | ||||||
| type IAnyBehaviorRecorder = BehaviorRecorder<any, any>; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 行为的基础信息 |  | ||||||
|  */ |  | ||||||
| class BehaviorInfo<E extends Record<EventType, any> = {}> extends Emitter<E> { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 图标名字 |  | ||||||
|      */ |  | ||||||
|     public iconName: string = "" |  | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 行为 ID |      * 行为 ID | ||||||
|      */ |      */ | ||||||
|     public behaviorId: string = ""; |     abstract id: string; | ||||||
|          |      | ||||||
|     /** |     /** | ||||||
|      * 行为名称 |      * 行为名称 | ||||||
|      */ |      */ | ||||||
|     public behaviorName: string = ""; |     abstract name: string; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 行为描述 |      * 行为描述 | ||||||
|      */ |      */ | ||||||
|     public describe?: string = ""; |     public describe?: string = ""; | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class BehaviorRecorder< |  | ||||||
|     P extends IBehaviorParameter = {}, |  | ||||||
|     E extends Record<EventType, any> = {} |  | ||||||
| > extends BehaviorInfo<{}> { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 命名序号 |  | ||||||
|      */ |  | ||||||
|     public nameIndex: number = 0; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 获取下一个 ID |  | ||||||
|      */ |  | ||||||
|     public getNextId() { |  | ||||||
|         return `B-${this.behaviorName}-${this.nameIndex ++}`; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 行为类型 |  | ||||||
|      */ |  | ||||||
|     public behavior: IBehaviorConstructor<P, E>; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 行为实例 |  | ||||||
|      */ |  | ||||||
|     public behaviorInstance: Behavior<P, E>; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 对象参数列表 |  | ||||||
|      */ |  | ||||||
|     public parameterOption: IBehaviorParameterOption<P>; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 获取参数列表的默认值 |  | ||||||
|      */ |  | ||||||
|     public getDefaultValue(): IBehaviorParameterValue<P> { |  | ||||||
|         let defaultObj = {} as IBehaviorParameterValue<P>; |  | ||||||
|         for (let key in this.parameterOption) { |  | ||||||
|             let defaultVal = this.parameterOption[key].defaultValue; |  | ||||||
|              |  | ||||||
|             defaultObj[key] = defaultVal as any; |  | ||||||
|             if (defaultObj[key] === undefined) { |  | ||||||
| 
 |  | ||||||
|                 switch (this.parameterOption[key].type) { |  | ||||||
|                     case "string": |  | ||||||
|                         defaultObj[key] = "" as any; |  | ||||||
|                         break; |  | ||||||
| 
 |  | ||||||
|                     case "number": |  | ||||||
|                         defaultObj[key] = 0 as any; |  | ||||||
|                         break; |  | ||||||
| 
 |  | ||||||
|                     case "boolean": |  | ||||||
|                         defaultObj[key] = false as any; |  | ||||||
|                         break; |  | ||||||
| 
 |  | ||||||
|                     case "vec": |  | ||||||
|                         defaultObj[key] = [0, 0, 0] as any; |  | ||||||
|                         break; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return defaultObj; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 创建一个新的行为实例 |  | ||||||
|      */ |  | ||||||
|     public new(): Behavior<P, E> { |  | ||||||
|         return new this.behavior(this.getNextId(), this.getDefaultValue()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public constructor(behavior: IBehaviorConstructor<P, E>) { |  | ||||||
|         super(); |  | ||||||
|         this.behavior = behavior; |  | ||||||
|         this.behaviorInstance = new this.behavior(this.getNextId(), {} as any); |  | ||||||
|         this.parameterOption = this.behaviorInstance.parameterOption; |  | ||||||
|         this.iconName = this.behaviorInstance.iconName; |  | ||||||
|         this.behaviorId = this.behaviorInstance.behaviorId; |  | ||||||
|         this.behaviorName = this.behaviorInstance.behaviorName; |  | ||||||
|         this.describe = this.behaviorInstance.describe; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 群体的某种行为 |  | ||||||
|  */ |  | ||||||
| class Behavior< |  | ||||||
|     P extends IBehaviorParameter = {}, |  | ||||||
|     E extends Record<EventType, any> = {} |  | ||||||
| > extends BehaviorInfo<E> { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 用户自定义名字 |  | ||||||
|      */ |  | ||||||
|     public name: string = ""; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 实例 ID |  | ||||||
|      */ |  | ||||||
|     public id: string = ""; |  | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 优先级 |      * 优先级 | ||||||
|      * 值越大执行顺序越靠后 |      * 值越大执行顺序越靠后 | ||||||
|      */ |      */ | ||||||
|     public priority: number = 0; |     public priority?: number = 0; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 行为参数 |      * 行为参数 | ||||||
|      */ |      */ | ||||||
|     public parameter: IBehaviorParameterValue<P>; |     abstract parameter?: P; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 对象参数列表 |  | ||||||
|      */ |  | ||||||
|     public parameterOption: IBehaviorParameterOption<P> = {} as any; |  | ||||||
| 
 |  | ||||||
|     public constructor(id: string, parameter: IBehaviorParameterValue<P>) { |  | ||||||
|         super(); |  | ||||||
|         this.id = id; |  | ||||||
|         this.parameter = parameter; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 相等校验 |  | ||||||
|      */ |  | ||||||
|     public equal(behavior: Behavior<any, any>): boolean { |  | ||||||
|         return this === behavior || this.id === behavior.id; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 删除标记 |  | ||||||
|      */ |  | ||||||
|     private deleteFlag: boolean = false; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 标记对象被删除 |  | ||||||
|      */ |  | ||||||
|     public markDelete() { |  | ||||||
|         this.deleteFlag = true; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 是否被删除 |  | ||||||
|      */ |  | ||||||
|     public isDeleted(): boolean { |  | ||||||
|         return this.deleteFlag; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 加载时调用 |  | ||||||
|      */ |  | ||||||
|     public load(model: Model): void {} |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 卸载时调用 |  | ||||||
|      */ |  | ||||||
|     public unload(model: Model): void {} |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 挂载时调用 |  | ||||||
|      */ |  | ||||||
|     public mount(group: Group, model: Model): void {} |  | ||||||
| 
 |  | ||||||
|      /** |  | ||||||
|       * 挂载时调用 |  | ||||||
|       */ |  | ||||||
|     public unmount(group: Group, model: Model): void {} |  | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 全部影响作用前 |      * 全部影响作用前 | ||||||
| @ -357,8 +67,5 @@ class Behavior< | |||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export { | export { Behavior }; | ||||||
|     Behavior, BehaviorRecorder, IBehaviorParameterOption, IBehaviorParameterOptionItem, |  | ||||||
|     IAnyBehavior, IAnyBehaviorRecorder |  | ||||||
| }; |  | ||||||
| export default { Behavior }; | export default { Behavior }; | ||||||
| @ -60,36 +60,22 @@ class CtrlObject extends LabelObject { | |||||||
|         return this === obj || this.id === obj.id; |         return this === obj || this.id === obj.id; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * 标记对象被删除 |  | ||||||
|      */ |  | ||||||
|     public markDelete() { |  | ||||||
|         this.deleteFlag = true; |  | ||||||
|     }; |  | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 删除标记 |      * 删除标记 | ||||||
|      */ |      */ | ||||||
|     private deleteFlag: boolean = false; |     private deleteFlag: boolean = false; | ||||||
| 
 | 
 | ||||||
|     /** |      /** | ||||||
|      * 检测是否被删除 |       * 是否被删除 | ||||||
|      */ |       */ | ||||||
|     public testDelete() { |     public isDeleted(): boolean { | ||||||
|  |         if (this.deleteFlag) return true; | ||||||
|         for (let i = 0; i < this.model.objectPool.length; i++) { |         for (let i = 0; i < this.model.objectPool.length; i++) { | ||||||
|             if (this.model.objectPool[i].equal(this)) { |             if (this.model.objectPool[i].equal(this)) return false; | ||||||
|                 this.deleteFlag = false; |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|         this.deleteFlag = true; |         this.deleteFlag = true; | ||||||
|     } |         return true; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 是否被删除 |  | ||||||
|      */ |  | ||||||
|     public isDeleted(): boolean { |  | ||||||
|         return this.deleteFlag; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -22,7 +22,7 @@ class Group extends CtrlObject { | |||||||
|     /** |     /** | ||||||
|      * 个体生成方式 |      * 个体生成方式 | ||||||
|      */ |      */ | ||||||
|     public genMethod: GenMod = GenMod.Range; |     public genMethod: GenMod = GenMod.Point; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 生成位置坐标 |      * 生成位置坐标 | ||||||
|  | |||||||
| @ -1,3 +1,4 @@ | |||||||
|  | 
 | ||||||
| import { Individual } from "./Individual"; | import { Individual } from "./Individual"; | ||||||
| import { Group } from "./Group"; | import { Group } from "./Group"; | ||||||
| import { Range } from "./Range"; | import { Range } from "./Range"; | ||||||
| @ -5,14 +6,18 @@ import { Emitter, EventType, EventMixin } from "./Emitter"; | |||||||
| import { CtrlObject } from "./CtrlObject"; | import { CtrlObject } from "./CtrlObject"; | ||||||
| import { ObjectID, AbstractRenderer } from "./Renderer"; | import { ObjectID, AbstractRenderer } from "./Renderer"; | ||||||
| import { Label } from "./Label"; | import { Label } from "./Label"; | ||||||
| import { Behavior, IAnyBehavior, IAnyBehaviorRecorder } from "./Behavior"; |  | ||||||
| 
 | 
 | ||||||
| type ModelEvent = { | type ModelEvent = { | ||||||
|     loop: number; |     loop: number; | ||||||
|  |     groupAdd: Group; | ||||||
|  |     rangeAdd: Range; | ||||||
|  |     labelAdd: Label; | ||||||
|  |     labelDelete: Label; | ||||||
|     labelChange: Label[]; |     labelChange: Label[]; | ||||||
|  |     objectAdd: CtrlObject; | ||||||
|  |     objectDelete: CtrlObject[]; | ||||||
|     objectChange: CtrlObject[]; |     objectChange: CtrlObject[]; | ||||||
|     individualChange: Group; |     individualChange: Group; | ||||||
|     behaviorChange: IAnyBehavior; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -63,6 +68,7 @@ class Model extends Emitter<ModelEvent> { | |||||||
|         console.log(`Model: Creat label with id ${this.idIndex}`); |         console.log(`Model: Creat label with id ${this.idIndex}`); | ||||||
|         let label = new Label(this, this.nextId("L"), name); |         let label = new Label(this, this.nextId("L"), name); | ||||||
|         this.labelPool.push(label); |         this.labelPool.push(label); | ||||||
|  |         this.emit("labelAdd", label); | ||||||
|         this.emit("labelChange", this.labelPool); |         this.emit("labelChange", this.labelPool); | ||||||
|         return label; |         return label; | ||||||
|     } |     } | ||||||
| @ -91,6 +97,7 @@ class Model extends Emitter<ModelEvent> { | |||||||
|             this.labelPool.splice(index, 1); |             this.labelPool.splice(index, 1); | ||||||
|             deletedLabel.testDelete(); |             deletedLabel.testDelete(); | ||||||
|             console.log(`Model: Delete label ${deletedLabel.name ?? deletedLabel.id}`); |             console.log(`Model: Delete label ${deletedLabel.name ?? deletedLabel.id}`); | ||||||
|  |             this.emit("labelDelete", deletedLabel); | ||||||
|             this.emit("labelChange", this.labelPool); |             this.emit("labelChange", this.labelPool); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -128,6 +135,8 @@ class Model extends Emitter<ModelEvent> { | |||||||
|         console.log(`Model: Creat group with id ${this.idIndex}`); |         console.log(`Model: Creat group with id ${this.idIndex}`); | ||||||
|         let group = new Group(this, this.nextId("G")); |         let group = new Group(this, this.nextId("G")); | ||||||
|         this.objectPool.push(group); |         this.objectPool.push(group); | ||||||
|  |         this.emit("groupAdd", group); | ||||||
|  |         this.emit("objectAdd", group); | ||||||
|         this.emit("objectChange", this.objectPool); |         this.emit("objectChange", this.objectPool); | ||||||
|         return group; |         return group; | ||||||
|     } |     } | ||||||
| @ -139,6 +148,8 @@ class Model extends Emitter<ModelEvent> { | |||||||
|         console.log(`Model: Creat range with id ${this.idIndex}`); |         console.log(`Model: Creat range with id ${this.idIndex}`); | ||||||
|         let range = new Range(this, this.nextId("R")); |         let range = new Range(this, this.nextId("R")); | ||||||
|         this.objectPool.push(range); |         this.objectPool.push(range); | ||||||
|  |         this.emit("rangeAdd", range); | ||||||
|  |         this.emit("objectAdd", range); | ||||||
|         this.emit("objectChange", this.objectPool); |         this.emit("objectChange", this.objectPool); | ||||||
|         return range; |         return range; | ||||||
|     } |     } | ||||||
| @ -165,7 +176,6 @@ class Model extends Emitter<ModelEvent> { | |||||||
| 
 | 
 | ||||||
|             if (needDeleted) { |             if (needDeleted) { | ||||||
|                 deletedObject.push(currentObject); |                 deletedObject.push(currentObject); | ||||||
|                 currentObject.markDelete(); |  | ||||||
|                 return false; |                 return false; | ||||||
|             } else { |             } else { | ||||||
|                 return true; |                 return true; | ||||||
| @ -174,68 +184,12 @@ class Model extends Emitter<ModelEvent> { | |||||||
| 
 | 
 | ||||||
|         if (deletedObject.length) { |         if (deletedObject.length) { | ||||||
|             console.log(`Model: Delete object ${deletedObject.map((object) => object.id).join(", ")}`); |             console.log(`Model: Delete object ${deletedObject.map((object) => object.id).join(", ")}`); | ||||||
|  |             this.emit("objectDelete", deletedObject); | ||||||
|             this.emit("objectChange", this.objectPool); |             this.emit("objectChange", this.objectPool); | ||||||
|         } |         } | ||||||
|         return deletedObject; |         return deletedObject; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * 行为池 |  | ||||||
|      */ |  | ||||||
|     public behaviorPool: IAnyBehavior[] = []; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 添加一个行为 |  | ||||||
|      */ |  | ||||||
|     public addBehavior<B extends IAnyBehaviorRecorder>(recorder: B): B["behaviorInstance"] { |  | ||||||
|         let behavior = recorder.new(); |  | ||||||
|         behavior.load(this); |  | ||||||
|         this.behaviorPool.push(behavior); |  | ||||||
|         console.log(`Model: Add ${behavior.behaviorName} behavior ${behavior.id}`); |  | ||||||
|         this.emit("behaviorChange", behavior); |  | ||||||
|         return behavior; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 通过 ID 获取行为 |  | ||||||
|      */ |  | ||||||
|     public getBehaviorById(id: ObjectID): IAnyBehavior | undefined { |  | ||||||
|         for (let i = 0; i < this.behaviorPool.length; i++) { |  | ||||||
|             if (this.behaviorPool[i].id.toString() === id.toString()) { |  | ||||||
|                 return this.behaviorPool[i]; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 搜索并删除一个 Behavior |  | ||||||
|      * @param name 搜索值 |  | ||||||
|      */ |  | ||||||
|     public deleteBehavior(name: IAnyBehavior | ObjectID) { |  | ||||||
|         let deletedBehavior: IAnyBehavior | undefined; |  | ||||||
|         let index = 0; |  | ||||||
| 
 |  | ||||||
|         for (let i = 0; i < this.behaviorPool.length; i++) { |  | ||||||
|             if (name instanceof Behavior) { |  | ||||||
|                 if (this.behaviorPool[i].equal(name)) { |  | ||||||
|                     deletedBehavior = this.behaviorPool[i]; |  | ||||||
|                     index = i; |  | ||||||
|                 } |  | ||||||
|             } else if (name === this.behaviorPool[i].id) { |  | ||||||
|                 deletedBehavior = this.behaviorPool[i]; |  | ||||||
|                 index = i; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (deletedBehavior) { |  | ||||||
|             this.behaviorPool.splice(index, 1); |  | ||||||
|             deletedBehavior.unload(this); |  | ||||||
|             deletedBehavior.markDelete(); |  | ||||||
|             console.log(`Model: Delete behavior ${deletedBehavior.name ?? deletedBehavior.id}`); |  | ||||||
|             this.emit("behaviorChange", deletedBehavior); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |     /** | ||||||
|      * 渲染器 |      * 渲染器 | ||||||
|      */ |      */ | ||||||
|  | |||||||
| @ -52,7 +52,7 @@ class ObjectCommand extends Component<IMixinStatusProps> { | |||||||
| 					this.props.status ? this.props.status.newRange() : undefined; | 					this.props.status ? this.props.status.newRange() : undefined; | ||||||
| 				}} | 				}} | ||||||
| 			> | 			> | ||||||
| 				<Icon iconName="ProductVariant"></Icon> | 				<Icon iconName="CubeShape"></Icon> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div | 			<div | ||||||
| 				className="command-item red" | 				className="command-item red" | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user