Feature/external layout options (#3178)

Improve scene selection and help text for an external layout
This commit is contained in:
Clément Pasteau
2021-10-21 14:18:44 +02:00
committed by GitHub
parent 3b73b5eb6d
commit c705f89de8
10 changed files with 228 additions and 145 deletions

View File

@@ -1,5 +1,5 @@
#if defined(GD_IDE_ONLY)
#include "GDCore/Project/ExternalEvents.h"
#include "ExternalEvents.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Events/Serialization.h"
@@ -48,4 +48,3 @@ void ExternalEvents::UnserializeFrom(gd::Project& project,
}
} // namespace gd
#endif

View File

@@ -3,12 +3,12 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#ifndef GDCORE_EXTERNALEVENTS_H
#define GDCORE_EXTERNALEVENTS_H
#include <ctime>
#include <memory>
#include <vector>
#include "GDCore/Events/EventsList.h"
#include "GDCore/String.h"
namespace gd {
@@ -135,4 +135,3 @@ struct ExternalEventsHasName
} // namespace gd
#endif // GDCORE_EXTERNALEVENTS_H
#endif

View File

@@ -5,6 +5,7 @@
*/
#include "GDCore/Project/ExternalLayout.h"
#include "GDCore/IDE/Dialogs/LayoutEditorCanvas/EditorSettings.h"
#include "GDCore/Project/InitialInstancesContainer.h"
#include "GDCore/Serialization/SerializerElement.h"
@@ -15,19 +16,15 @@ namespace gd {
void ExternalLayout::UnserializeFrom(const SerializerElement& element) {
name = element.GetStringAttribute("name", "", "Name");
instances.UnserializeFrom(element.GetChild("instances", 0, "Instances"));
#if defined(GD_IDE_ONLY)
editorSettings.UnserializeFrom(element.GetChild("editionSettings"));
#endif
associatedLayout = element.GetStringAttribute("associatedLayout");
}
#if defined(GD_IDE_ONLY)
void ExternalLayout::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", name);
instances.SerializeTo(element.AddChild("instances"));
editorSettings.SerializeTo(element.AddChild("editionSettings"));
element.SetAttribute("associatedLayout", associatedLayout);
}
#endif
} // namespace gd

View File

@@ -7,14 +7,13 @@
#ifndef GDCORE_EXTERNALLAYOUT_H
#define GDCORE_EXTERNALLAYOUT_H
#include <memory>
#include "GDCore/Project/InitialInstancesContainer.h"
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
}
#if defined(GD_IDE_ONLY)
#include "GDCore/IDE/Dialogs/LayoutEditorCanvas/EditorSettings.h"
#endif
namespace gd {
@@ -54,7 +53,6 @@ class GD_CORE_API ExternalLayout {
*/
gd::InitialInstancesContainer& GetInitialInstances() { return instances; }
#if defined(GD_IDE_ONLY)
/**
* \brief Get the user settings for the IDE.
*/
@@ -65,10 +63,7 @@ class GD_CORE_API ExternalLayout {
/**
* \brief Get the user settings for the IDE.
*/
gd::EditorSettings& GetAssociatedEditorSettings() {
return editorSettings;
}
#endif
gd::EditorSettings& GetAssociatedEditorSettings() { return editorSettings; }
/**
* \brief Get the name of the layout last used to edit the external layout.
@@ -80,15 +75,13 @@ class GD_CORE_API ExternalLayout {
*/
void SetAssociatedLayout(const gd::String& name) { associatedLayout = name; }
/** \name Serialization
*/
///@{
#if defined(GD_IDE_ONLY)
/** \name Serialization
*/
///@{
/**
* \brief Serialize external layout.
*/
void SerializeTo(SerializerElement& element) const;
#endif
/**
* \brief Unserialize the external layout.
@@ -99,9 +92,7 @@ class GD_CORE_API ExternalLayout {
private:
gd::String name;
gd::InitialInstancesContainer instances;
#if defined(GD_IDE_ONLY)
gd::EditorSettings editorSettings;
#endif
gd::String associatedLayout;
};

View File

@@ -8,7 +8,9 @@ import {
type RenderEditorContainerProps,
type RenderEditorContainerPropsWithRef,
} from './BaseEditor';
import LayoutChooserDialog from './LayoutChooserDialog';
import ExternalPropertiesDialog, {
type ExternalProperties,
} from './ExternalPropertiesDialog';
import Text from '../../UI/Text';
import { Line } from '../../UI/Grid';
@@ -20,7 +22,7 @@ const styles = {
};
type State = {|
layoutChooserOpen: boolean,
externalPropertiesDialogOpen: boolean,
|};
export class ExternalEventsEditorContainer extends React.Component<
@@ -30,7 +32,7 @@ export class ExternalEventsEditorContainer extends React.Component<
editor: ?EventsSheet;
state = {
layoutChooserOpen: false,
externalPropertiesDialogOpen: false,
};
shouldComponentUpdate(nextProps: RenderEditorContainerProps) {
@@ -65,6 +67,16 @@ export class ExternalEventsEditorContainer extends React.Component<
const { project } = this.props;
if (!project) return null;
const layoutName = this.getAssociatedLayoutName();
if (!layoutName) return null;
return project.getLayout(layoutName);
}
getAssociatedLayoutName(): ?string {
const { project } = this.props;
if (!project) return null;
const externalEvents = this.getExternalEvents();
if (!externalEvents) return null;
@@ -72,25 +84,26 @@ export class ExternalEventsEditorContainer extends React.Component<
if (!project.hasLayoutNamed(layoutName)) {
return null;
}
return project.getLayout(layoutName);
return layoutName;
}
setAssociatedLayout = (layoutName: string) => {
saveExternalProperties = (externalProps: ExternalProperties) => {
const externalEvents = this.getExternalEvents();
if (!externalEvents) return;
externalEvents.setAssociatedLayout(layoutName);
externalEvents.setAssociatedLayout(externalProps.layoutName);
this.setState(
{
layoutChooserOpen: false,
externalPropertiesDialogOpen: false,
},
() => this.updateToolbar()
);
};
openLayoutChooser = () => {
openExternalPropertiesDialog = () => {
this.setState({
layoutChooserOpen: true,
externalPropertiesDialogOpen: true,
});
};
@@ -124,7 +137,7 @@ export class ExternalEventsEditorContainer extends React.Component<
globalObjectsContainer={project}
objectsContainer={layout}
events={externalEvents.getEvents()}
onOpenSettings={this.openLayoutChooser}
onOpenSettings={this.openExternalPropertiesDialog}
onOpenExternalEvents={this.props.onOpenExternalEvents}
/>
)}
@@ -140,18 +153,24 @@ export class ExternalEventsEditorContainer extends React.Component<
<RaisedButton
label={<Trans>Choose the scene</Trans>}
primary
onClick={this.openLayoutChooser}
onClick={this.openExternalPropertiesDialog}
/>
</Line>
</PlaceholderMessage>
)}
<LayoutChooserDialog
title={<Trans>Choose the associated scene</Trans>}
helpText="You still need to add a Link event in the scene to import the external events"
open={this.state.layoutChooserOpen}
<ExternalPropertiesDialog
title={<Trans>Configure the external events</Trans>}
helpTexts={[
<Trans>
In order to use these external events, you still need to add a
"Link" event in the events sheet of the corresponding scene
</Trans>,
]}
open={this.state.externalPropertiesDialogOpen}
project={project}
onChoose={this.setAssociatedLayout}
onClose={() => this.setState({ layoutChooserOpen: false })}
onChoose={this.saveExternalProperties}
layoutName={this.getAssociatedLayoutName()}
onClose={() => this.setState({ externalPropertiesDialogOpen: false })}
/>
</div>
);

View File

@@ -13,7 +13,9 @@ import {
type RenderEditorContainerProps,
type RenderEditorContainerPropsWithRef,
} from './BaseEditor';
import LayoutChooserDialog from './LayoutChooserDialog';
import ExternalPropertiesDialog, {
type ExternalProperties,
} from './ExternalPropertiesDialog';
import { Line } from '../../UI/Grid';
import Text from '../../UI/Text';
import { prepareInstancesEditorSettings } from '../../InstancesEditor/InstancesEditorSettings';
@@ -26,7 +28,7 @@ const styles = {
};
type State = {|
layoutChooserOpen: boolean,
externalPropertiesDialogOpen: boolean,
|};
export class ExternalLayoutEditorContainer extends React.Component<
@@ -35,7 +37,7 @@ export class ExternalLayoutEditorContainer extends React.Component<
> {
editor: ?SceneEditor;
state = {
layoutChooserOpen: false,
externalPropertiesDialogOpen: false,
};
getProject(): ?gdProject {
@@ -98,6 +100,16 @@ export class ExternalLayoutEditorContainer extends React.Component<
const { project } = this.props;
if (!project) return null;
const layoutName = this.getAssociatedLayoutName();
if (!layoutName) return;
return project.getLayout(layoutName);
}
getAssociatedLayoutName(): ?string {
const { project } = this.props;
if (!project) return null;
const externalLayout = this.getExternalLayout();
if (!externalLayout) return null;
@@ -105,25 +117,26 @@ export class ExternalLayoutEditorContainer extends React.Component<
if (!project.hasLayoutNamed(layoutName)) {
return null;
}
return project.getLayout(layoutName);
return layoutName;
}
setAssociatedLayout = (layoutName: string) => {
saveExternalProperties = (externalProps: ExternalProperties) => {
const externalLayout = this.getExternalLayout();
if (!externalLayout) return;
externalLayout.setAssociatedLayout(layoutName);
externalLayout.setAssociatedLayout(externalProps.layoutName);
this.setState(
{
layoutChooserOpen: false,
externalPropertiesDialogOpen: false,
},
() => this.updateToolbar()
);
};
openLayoutChooser = () => {
openExternalPropertiesDialog = () => {
this.setState({
layoutChooserOpen: true,
externalPropertiesDialogOpen: true,
});
};
@@ -170,7 +183,7 @@ export class ExternalLayoutEditorContainer extends React.Component<
)
)
}
onOpenMoreSettings={this.openLayoutChooser}
onOpenMoreSettings={this.openExternalPropertiesDialog}
isActive={isActive}
/>
)}
@@ -186,17 +199,30 @@ export class ExternalLayoutEditorContainer extends React.Component<
<RaisedButton
label={<Trans>Choose the scene</Trans>}
primary
onClick={this.openLayoutChooser}
onClick={this.openExternalPropertiesDialog}
/>
</Line>
</PlaceholderMessage>
)}
<LayoutChooserDialog
title={<Trans>Choose the associated scene</Trans>}
open={this.state.layoutChooserOpen}
<ExternalPropertiesDialog
title={<Trans>Configure the external layout</Trans>}
helpTexts={[
<Trans>
In order to see your objects in the scene, you need to add an
action "Create objects from external layout" in your events sheet.
</Trans>,
<Trans>
You can also launch a preview from this external layout, but
remember that it will still create objects from the scene, as well
as trigger its events. Make sure to disable any action loading the
external layout before doing so to avoid having duplicate objects!
</Trans>,
]}
open={this.state.externalPropertiesDialogOpen}
project={project}
onChoose={this.setAssociatedLayout}
onClose={() => this.setState({ layoutChooserOpen: false })}
layoutName={this.getAssociatedLayoutName()}
onChoose={this.saveExternalProperties}
onClose={() => this.setState({ externalPropertiesDialogOpen: false })}
/>
</div>
);

View File

@@ -0,0 +1,111 @@
// @flow
import { Trans } from '@lingui/macro';
import * as React from 'react';
import FlatButton from '../../UI/FlatButton';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Radio from '@material-ui/core/Radio';
import RadioGroup from '@material-ui/core/RadioGroup';
import Dialog from '../../UI/Dialog';
import { mapFor } from '../../Utils/MapFor';
import Text from '../../UI/Text';
import BackgroundText from '../../UI/BackgroundText';
import { Line, Column } from '../../UI/Grid';
export type ExternalProperties = {|
layoutName: string,
|};
type Props = {|
open: boolean,
onChoose: ExternalProperties => void,
layoutName?: ?string,
onClose: () => void,
project: gdProject,
title?: React.Node,
helpTexts?: Array<React.Node>,
|};
export default function ExternalPropertiesDialog({
open,
onChoose,
layoutName,
onClose,
project,
title,
helpTexts,
}: Props) {
const initialLayoutName = layoutName || '';
const [selectedLayoutName, setSelectedLayoutName] = React.useState<string>(
initialLayoutName
);
const onClick = React.useCallback(
() => {
const externalProperties: ExternalProperties = {
layoutName: selectedLayoutName,
};
onChoose(externalProperties);
},
[onChoose, selectedLayoutName]
);
const actions = [
<FlatButton
key="cancel"
label={<Trans>Cancel</Trans>}
primary={false}
onClick={onClose}
/>,
<FlatButton
key="choose"
label={<Trans>Choose</Trans>}
primary
keyboardFocused
onClick={onClick}
disabled={!selectedLayoutName}
/>,
];
const layoutNames = mapFor(0, project.getLayoutsCount(), i => {
return project.getLayoutAt(i).getName();
});
return (
<Dialog
actions={actions}
open={open}
title={title}
onRequestClose={onClose}
cannotBeDismissed={false}
maxWidth="sm"
>
<Column>
{helpTexts &&
helpTexts.map(helpText => (
<Line>
<BackgroundText>{helpText}</BackgroundText>
</Line>
))}
<Line>
<Text>
<Trans>Choose the associated scene:</Trans>
</Text>
</Line>
<RadioGroup
aria-label="Associated scene"
name="associated-layout"
value={selectedLayoutName}
onChange={event => setSelectedLayoutName(event.target.value)}
>
{layoutNames.map(name => (
<FormControlLabel
key={name}
value={name}
control={<Radio color="primary" />}
label={name}
/>
))}
</RadioGroup>
</Column>
</Dialog>
);
}

View File

@@ -1,86 +0,0 @@
import { Trans } from '@lingui/macro';
import React, { Component } from 'react';
import FlatButton from '../../UI/FlatButton';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Radio from '@material-ui/core/Radio';
import RadioGroup from '@material-ui/core/RadioGroup';
import Dialog from '../../UI/Dialog';
import { mapFor } from '../../Utils/MapFor';
import Text from '../../UI/Text';
export default class LayoutChooserDialog extends Component {
constructor(props) {
super(props);
this.state = {
selectedLayoutName: props.layoutName,
};
}
componentWillReceiveProps(newProps) {
if (newProps.layoutName !== this.props.layoutName) {
this.setState({
selectedLayoutName: newProps.layoutName,
});
}
}
chooseLayout(layoutName) {
this.setState({
selectedLayoutName: layoutName,
});
}
render() {
const actions = [
<FlatButton
key="cancel"
label={<Trans>Cancel</Trans>}
primary={false}
onClick={this.props.onClose}
/>,
<FlatButton
key="choose"
label={<Trans>Choose</Trans>}
primary={true}
keyboardFocused={true}
onClick={() => this.props.onChoose(this.state.selectedLayoutName)}
disabled={!this.state.selectedLayoutName}
/>,
];
const { project } = this.props;
const { selectedLayoutName } = this.state;
const layoutNames = mapFor(0, project.getLayoutsCount(), i => {
return project.getLayoutAt(i).getName();
});
return (
<Dialog
actions={actions}
open={this.props.open}
title={this.props.title}
onRequestClose={this.props.onClose}
cannotBeDismissed={false}
fullWidth
maxWidth="sm"
>
{this.props.helpText && <Text>{this.props.helpText}</Text>}
<RadioGroup
aria-label="Associated scene"
name="associated-layout"
value={selectedLayoutName}
onChange={event => this.chooseLayout(event.target.value)}
>
{layoutNames.map(name => (
<FormControlLabel
key={name}
value={name}
control={<Radio color="primary" />}
label={name}
/>
))}
</RadioGroup>
</Dialog>
);
}
}

View File

@@ -164,6 +164,14 @@ export class Toolbar extends PureComponent<Props> {
{ label: '400%', click: () => this.props.setZoomFactor(4.0) },
]}
/>
{this.props.onOpenSettings && <ToolbarSeparator />}
{this.props.onOpenSettings && (
<ToolbarIcon
onClick={this.props.onOpenSettings}
src="res/ribbon_default/pref32.png"
tooltip={t`Open settings`}
/>
)}
</ToolbarGroup>
</>
);

View File

@@ -57,7 +57,7 @@ import ObjectSelector from '../ObjectsList/ObjectSelector';
import InstancePropertiesEditor from '../InstancesEditor/InstancePropertiesEditor';
import SerializedObjectDisplay from './SerializedObjectDisplay';
import EventsTree from '../EventsSheet/EventsTree';
import LayoutChooserDialog from '../MainFrame/EditorContainers/LayoutChooserDialog';
import ExternalPropertiesDialog from '../MainFrame/EditorContainers/ExternalPropertiesDialog';
import InstructionEditor from '../EventsSheet/InstructionEditor';
import EventsSheet from '../EventsSheet';
import BehaviorsEditor from '../BehaviorsEditor';
@@ -2994,10 +2994,29 @@ storiesOf('OpenConfirmDialog', module)
/>
));
storiesOf('LayoutChooserDialog', module)
storiesOf('ExternalPropertiesDialog', module)
.addDecorator(muiDecorator)
.add('default', () => (
<LayoutChooserDialog open project={testProject.project} />
.add('with layout selection', () => (
<ExternalPropertiesDialog
title="Configure the properties"
open
onChoose={action('on choose')}
onClose={action('on close')}
project={testProject.project}
/>
))
.add('with help texts', () => (
<ExternalPropertiesDialog
title="Configure the properties"
open
onChoose={action('on choose')}
onClose={action('on close')}
project={testProject.project}
helpTexts={[
'This is a help text, remember to read it.',
"And there's another one!",
]}
/>
));
storiesOf('EventsTree', module)