Add open, save, close project buttons to newIDE and project manager icons

This commit is contained in:
Florian Rival
2017-05-21 22:10:10 +02:00
parent 6ea781533b
commit 6f4abd3eb9
15 changed files with 284 additions and 50 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

View File

@@ -25,7 +25,7 @@
<meta name="theme-color" content="#4ab0e4">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
</head>
<body>
<body oncontextmenu="return false;">
<!-- Root div used for React `App` component rendering-->
<div id="root"></div>

View File

@@ -37,6 +37,8 @@ export default class InstancesEditorContainer extends Component {
this.lastContextMenuY = e.offsetY;
if (this.props.onContextMenu)
this.props.onContextMenu(e.clientX, e.clientY);
return false;
});
this.pixiRenderer.view.addEventListener('click', e => {
this._onClick(e.offsetX, e.offsetY);
@@ -64,6 +66,7 @@ export default class InstancesEditorContainer extends Component {
);
gesture.panable(this.backgroundArea);
this.backgroundArea.on('mousedown', this._onBackgroundClicked);
this.backgroundArea.on('touchstart', this._onBackgroundClicked);
this.backgroundArea.on('panmove', event =>
this._onMakeSelectionRectangle(event.data.global.x, event.data.global.y));
this.backgroundArea.on('panend', event => this._onEndSelectionRectangle());

View File

@@ -120,6 +120,9 @@ export default class LayerRenderer {
renderedInstance._pixiObject.on('mousedown', () => {
this.onDownInstance(instance);
});
renderedInstance._pixiObject.on('touchstart', () => {
this.onDownInstance(instance);
});
renderedInstance._pixiObject.on('mouseout', () => {
this.onOutInstance(instance);
});

View File

@@ -51,7 +51,7 @@ export default class SelectionRectangle {
};
endSelectionRectangle = () => {
if (!this.selectionRectangleStart) return;
if (!this.selectionRectangleStart) return [];
this._instancesInSelectionRectangle.length = 0;
if (this.selectionRectangleStart.x > this.selectionRectangleEnd.x) {

View File

@@ -0,0 +1,60 @@
import React, { Component } from 'react';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
export default class ConfirmCloseDialog extends Component {
constructor(props) {
super(props);
this.state = {
open: false,
};
}
show(onHandleAnswer) {
this.onHandleAnswer = onHandleAnswer;
this.setState({
open: true,
});
}
handleCancel = () => {
this.setState({
open: false,
});
if (this.onHandleAnswer) this.onHandleAnswer(false);
}
handleClose = () => {
this.setState({
open: false,
});
if (this.onHandleAnswer) this.onHandleAnswer(true);
}
render() {
const actions = [
<FlatButton
label="Cancel"
primary={true}
onTouchTap={this.handleCancel}
/>,
<FlatButton
label="Close project"
primary={true}
onTouchTap={this.handleClose}
/>,
];
return (
<Dialog
title="Close project"
actions={actions}
modal={true}
open={this.state.open}
>
Any changes that has not been saved will be lost.
</Dialog>
);
}
}

View File

@@ -0,0 +1,13 @@
import { Component } from 'react';
import Window from '../Utils/Window';
export default class ProjectTitlebar extends Component {
render() {
const { project } = this.props;
const titleElements = ['GDevelop'];
if (project) titleElements.push(project.getProjectFile() || project.getName());
Window.setTitle(titleElements.join(' - '));
return null;
}
}

View File

@@ -28,6 +28,11 @@ export default class MainFrameToolbar extends Component {
<ToolbarIcon
onClick={this.props.toggleProjectManager}
src="res/ribbon_default/projectManager32.png"
disabled={!this.props.hasProject}
/>
<ToolbarIcon
onClick={this.props.openProject}
src="res/ribbon_default/open32.png"
/>
{this.isDev &&
<IconMenu

View File

@@ -8,6 +8,8 @@ import NavigationClose from 'material-ui/svg-icons/navigation/close';
import Toolbar from './Toolbar';
import StartPage from './StartPage';
import ProjectTitlebar from './ProjectTitlebar';
import ConfirmCloseDialog from './ConfirmCloseDialog';
import EventsSheetContainer from '../EventsSheet/EventsSheetContainer.js';
import SceneEditor from '../SceneEditor';
import ExternalLayoutEditor from '../SceneEditor/ExternalLayoutEditor';
@@ -27,6 +29,7 @@ import {
closeProjectTabs,
} from './EditorTabsHandler';
import FileOpener from '../Utils/FileOpener';
import FileWriter from '../Utils/FileWriter';
import fixtureGame from '../fixtures/fixture-game.json';
const gd = global.gd;
@@ -128,55 +131,77 @@ class MainFrame extends Component {
openExternalEvents = name => {
this.setState({
editorTabs: openEditorTab(this.state.editorTabs, name, () => (
<EventsSheetContainer
project={this.state.currentProject}
events={this.state.currentProject.getExternalEvents(name).getEvents()}
layout={this.state.currentProject.getLayoutAt(0)}
setToolbar={this.setEditorToolbar}
/>
), 'external events ' + name),
editorTabs: openEditorTab(
this.state.editorTabs,
name,
() => (
<EventsSheetContainer
project={this.state.currentProject}
events={this.state.currentProject
.getExternalEvents(name)
.getEvents()}
layout={this.state.currentProject.getLayoutAt(0)}
setToolbar={this.setEditorToolbar}
/>
),
'external events ' + name
),
});
};
openLayout = name => {
this.setState({
editorTabs: openEditorTab(this.state.editorTabs, name, () => (
<SceneEditor
project={this.state.currentProject}
layoutName={name}
setToolbar={this.setEditorToolbar}
onPreview={this.props.onPreview}
showPreviewButton
onEditObject={this.props.onEditObject}
/>
), 'layout ' + name),
editorTabs: openEditorTab(
this.state.editorTabs,
name,
() => (
<SceneEditor
project={this.state.currentProject}
layoutName={name}
setToolbar={this.setEditorToolbar}
onPreview={this.props.onPreview}
showPreviewButton
onEditObject={this.props.onEditObject}
/>
),
'layout ' + name
),
});
};
openExternalLayout = name => {
this.setState({
editorTabs: openEditorTab(this.state.editorTabs, name, () => (
<ExternalLayoutEditor
project={this.state.currentProject}
externalLayoutName={name}
setToolbar={this.setEditorToolbar}
onPreview={this.props.onPreview}
showPreviewButton
onEditObject={this.props.onEditObject}
/>
), 'external layout ' + name),
editorTabs: openEditorTab(
this.state.editorTabs,
name,
() => (
<ExternalLayoutEditor
project={this.state.currentProject}
externalLayoutName={name}
setToolbar={this.setEditorToolbar}
onPreview={this.props.onPreview}
showPreviewButton
onEditObject={this.props.onEditObject}
/>
),
'external layout ' + name
),
});
};
openStartPage = () => {
this.setState({
editorTabs: openEditorTab(this.state.editorTabs, 'Start Page', () => (
<StartPage
setToolbar={this.setEditorToolbar}
onOpen={this._onOpenFromFile}
/>
), 'start page'),
editorTabs: openEditorTab(
this.state.editorTabs,
'Start Page',
() => (
<StartPage
setToolbar={this.setEditorToolbar}
onOpen={this._onOpenFromFile}
/>
),
'start page'
),
});
};
@@ -194,7 +219,10 @@ class MainFrame extends Component {
this.setState(
{
loadingProject: true,
editorTabs: closeProjectTabs(this.state.editorTabs, this.state.currentProject)
editorTabs: closeProjectTabs(
this.state.editorTabs,
this.state.currentProject
),
},
() =>
setTimeout(() => {
@@ -204,6 +232,8 @@ class MainFrame extends Component {
this.loadFullProject(serializedObject, () => {
serializedObject.delete();
this.state.currentProject.setProjectFile(filepath);
this.setState({
loadingProject: false,
projectManagerOpen: true,
@@ -216,9 +246,53 @@ class MainFrame extends Component {
});
};
_onSaveToFile = () => {
const filepath = this.state.currentProject.getProjectFile();
if (!filepath) {
console.warn('Unimplemented Saveas'); // TODO
return;
}
FileWriter.writeProjectJSONFile(
this.state.currentProject,
filepath,
err => {
if (err) {
//TODO: Error displayed to user with a generic component
console.error('Unable to write project', err);
return;
}
}
);
};
_onCloseProject = () => {
if (!this.state.currentProject) return;
this.confirmCloseDialog.show(closeProject => {
if (!closeProject || !this.state.currentProject) return;
this.setState(
{
projectManagerOpen: false,
editorTabs: closeProjectTabs(
this.state.editorTabs,
this.state.currentProject
),
},
() => {
this.state.currentProject.delete();
this.setState({
currentProject: null,
})
}
);
});
};
_onChangeEditorTab = value => {
this.setState({
editorTabs: changeCurrentTab(this.state.editorTabs, value)
editorTabs: changeCurrentTab(this.state.editorTabs, value),
});
};
@@ -241,6 +315,7 @@ class MainFrame extends Component {
return (
<MuiThemeProvider muiTheme={defaultTheme}>
<div className="main-frame">
<ProjectTitlebar project={this.state.currentProject} />
<Drawer open={this.state.projectManagerOpen}>
<EditorBar
title={currentProject ? currentProject.getName() : 'No project'}
@@ -257,11 +332,15 @@ class MainFrame extends Component {
onOpenExternalEvents={this.openExternalEvents}
onOpenLayout={this.openLayout}
onOpenExternalLayout={this.openExternalLayout}
onSaveProject={this._onSaveToFile}
onCloseProject={this._onCloseProject}
/>}
</Drawer>
<Toolbar
ref={toolbar => this.toolbar = toolbar}
hasProject={!!this.state.currentProject}
toggleProjectManager={this.toggleProjectManager}
openProject={this._onOpenFromFile}
loadBuiltinGame={this.loadBuiltinGame}
requestUpdate={this.requestUpdate}
/>
@@ -285,6 +364,9 @@ class MainFrame extends Component {
))}
</Tabs>
<LoaderModal show={this.state.loadingProject || this.props.loading} />
<ConfirmCloseDialog
ref={confirmCloseDialog => this.confirmCloseDialog = confirmCloseDialog}
/>
</div>
</MuiThemeProvider>
);

View File

@@ -0,0 +1,20 @@
import React, { Component } from 'react';
import IconButton from 'material-ui/IconButton';
export default class ListIcon extends Component {
render() {
return (
<IconButton
{...this.props}
iconStyle={{
//Properly align icons with the rest of the list
marginLeft: -16,
marginTop: -16,
filter: this.props.disabled ? 'grayscale(100%)' : undefined,
}}
>
<img alt={this.props.tooltip} src={this.props.src} />
</IconButton>
);
}
}

View File

@@ -1,8 +1,7 @@
import React from 'react';
import { List, ListItem } from 'material-ui/List';
import ContentInbox from 'material-ui/svg-icons/content/inbox';
import ContentSend from 'material-ui/svg-icons/content/send';
import { mapFor } from '../Utils/MapFor';
import ListIcon from './ListIcon';
export default class ProjectManager extends React.Component {
state = {};
@@ -12,10 +11,34 @@ export default class ProjectManager extends React.Component {
return (
<List>
<ListItem primaryText="Resources" leftIcon={<ContentSend />} />
<ListItem
primaryText="Options"
leftIcon={<ListIcon src="res/ribbon_default/new32.png" />}
initiallyOpen={true}
primaryTogglesNestedList={true}
autoGenerateNestedIndicator={true}
nestedItems={[
<ListItem
key="save"
primaryText="Save"
leftIcon={<ListIcon src="res/ribbon_default/save32.png" />}
onTouchTap={() => this.props.onSaveProject()}
/>,
<ListItem
key="close"
primaryText="Close"
leftIcon={<ListIcon src="res/ribbon_default/close32.png" />}
onTouchTap={() => this.props.onCloseProject()}
/>,
]}
/>
<ListItem
primaryText="Resources"
leftIcon={<ListIcon src="res/ribbon_default/image32.png" />}
/>
<ListItem
primaryText="Scenes"
leftIcon={<ContentInbox />}
leftIcon={<ListIcon src="res/ribbon_default/sceneadd32.png" />}
initiallyOpen={true}
primaryTogglesNestedList={true}
autoGenerateNestedIndicator={true}
@@ -26,7 +49,7 @@ export default class ProjectManager extends React.Component {
<ListItem
key={i}
primaryText={name}
leftIcon={<ContentInbox />}
leftIcon={<ListIcon src="res/ribbon_default/sceneadd32.png" />}
onTouchTap={() => this.props.onOpenLayout(name)}
/>
);
@@ -34,8 +57,8 @@ export default class ProjectManager extends React.Component {
/>
<ListItem
primaryText="External events"
leftIcon={<ContentInbox />}
initiallyOpen={true}
leftIcon={<ListIcon src="res/ribbon_default/externalevents32.png" />}
initiallyOpen={false}
primaryTogglesNestedList={true}
autoGenerateNestedIndicator={true}
nestedItems={mapFor(0, project.getExternalEventsCount(), i => {
@@ -45,7 +68,7 @@ export default class ProjectManager extends React.Component {
<ListItem
key={i}
primaryText={name}
leftIcon={<ContentInbox />}
leftIcon={<ListIcon src="res/ribbon_default/externalevents32.png" />}
onTouchTap={() => this.props.onOpenExternalEvents(name)}
/>
);
@@ -53,8 +76,8 @@ export default class ProjectManager extends React.Component {
/>
<ListItem
primaryText="External layouts"
leftIcon={<ContentInbox />}
initiallyOpen={true}
leftIcon={<ListIcon src="res/ribbon_default/externallayout32.png" />}
initiallyOpen={false}
primaryTogglesNestedList={true}
autoGenerateNestedIndicator={true}
nestedItems={mapFor(0, project.getExternalLayoutsCount(), i => {
@@ -64,7 +87,7 @@ export default class ProjectManager extends React.Component {
<ListItem
key={i}
primaryText={name}
leftIcon={<ContentInbox />}
leftIcon={<ListIcon src="res/ribbon_default/externallayout32.png" />}
onTouchTap={() => this.props.onOpenExternalLayout(name)}
/>
);

View File

@@ -5,13 +5,13 @@ export default class ToolbarIcon extends Component {
render() {
return (
<IconButton
{...this.props}
iconStyle={{
//Properly align icons with the rest of the toolbar
marginLeft: -4,
marginTop: -4,
filter: this.props.disabled ? 'grayscale(100%)' : undefined,
}}
{...this.props}
>
<img alt={this.props.tooltip} src={this.props.src} />
</IconButton>

View File

@@ -0,0 +1,16 @@
import {serializeToJSObject} from './Serializer';
import optionalRequire from './OptionalRequire.js';
const fs = optionalRequire('fs');
export default class FileOpener {
static writeProjectJSONFile(project, filepath, cb) {
if (!fs) return cb('Not supported');
try {
const content = JSON.stringify(serializeToJSObject(project));
fs.writeFile(filepath, content, cb);
} catch(e) {
return cb(e);
}
}
}

View File

@@ -7,6 +7,15 @@ if (electron) {
}
export default class Window {
static setTitle(title) {
if (electron) {
const browserWindow = electron.remote.getCurrentWindow();
browserWindow.setTitle(title);
} else {
document.title = title;
}
}
static setBounds(x, y, width, height) {
if (!electron) return;