import React from "react"; import { action, computed, makeObservable } from "mobx"; import { observer } from "mobx-react"; import { IEezObject } from "project-editor/core/object"; import { TreeAdapter, TreeObjectAdapter } from "project-editor/core/objectAdapter"; import { IPanel } from "project-editor/store"; import { Tree } from "project-editor/ui-components/Tree"; import { ProjectContext } from "project-editor/project/context"; import { ProjectEditor } from "project-editor/project-editor-interface"; import type { PageTabState } from "project-editor/features/page/PageEditor"; import type { Widget } from "project-editor/flow/component"; import classNames from "classnames"; import { IconAction } from "eez-studio-ui/action"; import { Body, ToolbarHeader, VerticalHeaderWithBody } from "eez-studio-ui/header-with-body"; import { Toolbar } from "eez-studio-ui/toolbar"; import { visitObjects } from "project-editor/core/search"; import { CommentActionComponent } from "project-editor/flow/components/actions"; //////////////////////////////////////////////////////////////////////////////// const LOCK_ICON = ( ); const UNLOCK_ICON = ( ); const EYE_OPEN_ICON = ( ); const EYE_CLOSE_ICON = ( ); export const PageStructure = observer( class PageStructure extends React.Component implements IPanel { static contextType = ProjectContext; declare context: React.ContextType; constructor(props: any) { super(props); makeObservable(this, { pageTabState: computed, componentContainerDisplayItem: computed, treeAdapter: computed, isAnyLocked: computed, isAnyHidden: computed }); } componentDidMount() { this.context.navigationStore.mountPanel(this); } componentWillUnmount() { this.context.navigationStore.unmountPanel(this); } get pageTabState() { const editor = this.context.editorsStore.activeEditor; if (!editor) { return undefined; } const object = editor.object; if (!(object instanceof ProjectEditor.PageClass)) { return undefined; } return editor.state as PageTabState; } get componentContainerDisplayItem() { if (!this.pageTabState) { return undefined; } return this.pageTabState.widgetContainer; } get treeAdapter() { if (!this.componentContainerDisplayItem) { return null; } return new TreeAdapter( this.componentContainerDisplayItem, undefined, (object: IEezObject) => { return object instanceof ProjectEditor.WidgetClass; }, true, undefined, undefined, undefined, undefined, undefined, undefined, // Argument hideRootItem is true in case of LVGL page, ie root LVGLScreenWidget is hidden for the users. this.context.projectTypeTraits.isLVGL && this.pageTabState?.page.lvglScreenWidget ? true : false ); } // interface IPanel implementation get selectedObject() { return this.selectedObjects[0]; } get selectedObjects() { const selectedObjects = this.componentContainerDisplayItem && this.componentContainerDisplayItem.selectedObjects; if (selectedObjects && selectedObjects.length > 0) { return selectedObjects; } if (this.pageTabState) { return [this.pageTabState.page]; } return []; } canCut() { return this.treeAdapter ? this.treeAdapter.canCut() : false; } cutSelection() { this.treeAdapter!.cutSelection(); } canCopy() { return this.treeAdapter ? this.treeAdapter.canCopy() : false; } copySelection() { this.treeAdapter!.copySelection(); } canPaste() { return this.treeAdapter ? this.treeAdapter.canPaste() : false; } pasteSelection() { this.treeAdapter!.pasteSelection(); } canDelete() { return this.treeAdapter ? this.treeAdapter.canDelete() : false; } deleteSelection() { this.treeAdapter!.deleteSelection(); } selectAll() { this.treeAdapter!.selectItems( this.treeAdapter!.allRows.map(row => row.item) ); } onFocus = () => { this.context.navigationStore.setSelectedPanel(this); }; get isAnyLocked() { if (!this.treeAdapter) { return false; } return ( this.treeAdapter.allRows.find( row => ((row.item as TreeObjectAdapter).object as Widget) .locked ) != undefined ); } onLockAll = action(() => { if (!this.treeAdapter) { return; } this.context.undoManager.setCombineCommands(true); this.treeAdapter.allRows.forEach(row => { const widget = (row.item as TreeObjectAdapter).object as Widget; if (!widget.locked) { this.context.updateObject(widget, { locked: true }); } }); this.context.undoManager.setCombineCommands(false); }); onUnlockAll = action(() => { if (!this.treeAdapter) { return; } this.context.undoManager.setCombineCommands(true); this.treeAdapter.allRows.forEach(row => { const widget = (row.item as TreeObjectAdapter).object as Widget; if (widget.locked) { this.context.updateObject(widget, { locked: false }); } }); this.context.undoManager.setCombineCommands(false); }); get isAnyHidden() { if (!this.treeAdapter) { return false; } return ( this.treeAdapter.allRows.find( row => ((row.item as TreeObjectAdapter).object as Widget) .hiddenInEditor ) != undefined ); } onHideAll = action(() => { if (!this.treeAdapter) { return; } this.context.undoManager.setCombineCommands(true); this.treeAdapter.allRows.forEach(row => { const widget = (row.item as TreeObjectAdapter).object as Widget; if (!widget.hiddenInEditor) { this.context.updateObject(widget, { hiddenInEditor: true }); } }); this.context.undoManager.setCombineCommands(false); }); onShowAll = action(() => { if (!this.treeAdapter) { return; } this.context.undoManager.setCombineCommands(true); this.treeAdapter.allRows.forEach(row => { const widget = (row.item as TreeObjectAdapter).object as Widget; if (widget.hiddenInEditor) { this.context.updateObject(widget, { hiddenInEditor: false }); } }); this.context.undoManager.setCombineCommands(false); }); renderItem = (itemId: string) => { if (!this.treeAdapter) { return null; } const item = this.treeAdapter.getItemFromId(itemId); if (!item) { return null; } const widget = item.object as Widget; return ( {this.treeAdapter.itemToString(item)} this.context.updateObject(widget, { locked: !widget.locked }) )} style={{ visibility: widget.locked ? "visible" : "hidden" }} /> { const hiddenInEditor = !widget.hiddenInEditor; this.context.undoManager.setCombineCommands( true ); this.context.updateObject(widget, { hiddenInEditor }); const childWidgets: Widget[] = []; for (const object of visitObjects(widget)) { if ( object instanceof ProjectEditor.WidgetClass ) { childWidgets.push(object); } } childWidgets.forEach(childWidget => this.context.updateObject(childWidget, { hiddenInEditor }) ); this.context.undoManager.setCombineCommands( false ); })} style={{ visibility: widget.hiddenInEditor ? "visible" : "hidden" }} /> ); }; render() { return this.treeAdapter ? ( {this.isAnyHidden ? ( ) : null} ) : (
e.preventDefault()} >
); } } ); export const ActionComponents = observer( class ActionComponents extends React.Component implements IPanel { static contextType = ProjectContext; declare context: React.ContextType; constructor(props: any) { super(props); makeObservable(this, { pageTabState: computed, componentContainerDisplayItem: computed, treeAdapter: computed }); } componentDidMount() { this.context.navigationStore.mountPanel(this); } componentWillUnmount() { this.context.navigationStore.unmountPanel(this); } get pageTabState() { const editor = this.context.editorsStore.activeEditor; if (!editor) { return undefined; } const object = editor.object; if (!(object instanceof ProjectEditor.PageClass)) { return undefined; } return editor.state as PageTabState; } get componentContainerDisplayItem() { if (!this.pageTabState) { return undefined; } return this.pageTabState.widgetContainer; } get treeAdapter() { if (!this.componentContainerDisplayItem) { return null; } return new TreeAdapter( this.componentContainerDisplayItem, undefined, (object: IEezObject) => { return ( object instanceof ProjectEditor.ActionComponentClass && !(object instanceof CommentActionComponent) ); }, true ); } // interface IPanel implementation get selectedObject() { return this.selectedObjects[0]; } get selectedObjects() { const selectedObjects = this.componentContainerDisplayItem && this.componentContainerDisplayItem.selectedObjects; if (selectedObjects && selectedObjects.length > 0) { return selectedObjects; } if (this.pageTabState) { return [this.pageTabState.page]; } return []; } canCut() { return this.treeAdapter ? this.treeAdapter.canCut() : false; } cutSelection() { this.treeAdapter!.cutSelection(); } canCopy() { return this.treeAdapter ? this.treeAdapter.canCopy() : false; } copySelection() { this.treeAdapter!.copySelection(); } canPaste() { return this.treeAdapter ? this.treeAdapter.canPaste() : false; } pasteSelection() { this.treeAdapter!.pasteSelection(); } canDelete() { return this.treeAdapter ? this.treeAdapter.canDelete() : false; } deleteSelection() { this.treeAdapter!.deleteSelection(); } onFocus = () => { this.context.navigationStore.setSelectedPanel(this); }; renderItem = (itemId: string) => { if (!this.treeAdapter) { return null; } const item = this.treeAdapter.getItemFromId(itemId); if (!item) { return null; } return ( {this.treeAdapter.itemToString(item)} ); }; render() { return this.treeAdapter ? ( ) : null; } } );