mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Add open, save, close project buttons to newIDE and project manager icons
This commit is contained in:
Binary file not shown.
After Width: | Height: | Size: 413 B |
Binary file not shown.
After Width: | Height: | Size: 568 B |
@@ -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>
|
||||
|
||||
|
@@ -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());
|
||||
|
@@ -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);
|
||||
});
|
||||
|
@@ -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) {
|
||||
|
60
newIDE/app/src/MainFrame/ConfirmCloseDialog.js
Normal file
60
newIDE/app/src/MainFrame/ConfirmCloseDialog.js
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
13
newIDE/app/src/MainFrame/ProjectTitlebar.js
Normal file
13
newIDE/app/src/MainFrame/ProjectTitlebar.js
Normal 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;
|
||||
}
|
||||
}
|
@@ -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
|
||||
|
@@ -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>
|
||||
);
|
||||
|
20
newIDE/app/src/ProjectManager/ListIcon.js
Normal file
20
newIDE/app/src/ProjectManager/ListIcon.js
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
@@ -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)}
|
||||
/>
|
||||
);
|
||||
|
@@ -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>
|
||||
|
16
newIDE/app/src/Utils/FileWriter.js
Normal file
16
newIDE/app/src/Utils/FileWriter.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user