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;
}
}
);