Skip to content

Commit eb260f3

Browse files
Add dialogs for open/save project (#80)
1 parent f8a3af7 commit eb260f3

File tree

6 files changed

+180
-18
lines changed

6 files changed

+180
-18
lines changed

main.js

+107-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const {
2-
app, BrowserWindow, Menu, ipcMain,
2+
app, BrowserWindow, Menu, ipcMain, dialog,
33
} = require('electron');
44
const { spawn } = require('child_process');
55
const path = require('node:path');
@@ -8,13 +8,18 @@ const Store = require('electron-store');
88
const log = require('electron-log');
99
const config = require('./rpe.config.json');
1010
const { kill } = require('./cleanup');
11+
const {
12+
openProjectRequest, saveProjectRequest, sendProjectData, fetchProjectData,
13+
} = require('./projectFile');
1114

1215
const logFormat = '[{h}:{i}:{s}.{ms}] [{level}] {text}';
1316
log.transports.console.format = logFormat;
1417
log.transports.file.format = logFormat;
1518
log.transports.file.fileName = 'rpe.log';
1619
log.transports.file.maxSize = 1024 * 1024 * 10; // 10MB
1720

21+
const untitled = 'Untitled';
22+
1823
const schema = {
1924
port: {
2025
type: 'number',
@@ -34,8 +39,86 @@ const schema = {
3439

3540
const store = new Store({ schema });
3641

42+
let projectMeta = {
43+
file: '',
44+
notes: '',
45+
lang: 0,
46+
name: '',
47+
changed: false,
48+
};
49+
3750
let mainWindow = null;
3851

52+
function sendProjectDataToRenderer() {
53+
mainWindow.webContents.send('projectData', projectMeta);
54+
}
55+
56+
function saveProjectClicked() {
57+
if (projectMeta.file === '') {
58+
const file = saveProjectRequest(mainWindow);
59+
if (file.length > 0) {
60+
projectMeta.file = file;
61+
projectMeta.changed = false;
62+
mainWindow.setTitle(`${path.basename(file)} - Rapid Power Estimator`);
63+
sendProjectData(projectMeta);
64+
}
65+
} else {
66+
projectMeta.changed = false;
67+
sendProjectData(projectMeta);
68+
}
69+
}
70+
71+
function projectSaved() {
72+
if (projectMeta.changed) {
73+
const result = dialog.showMessageBoxSync(mainWindow, {
74+
type: 'question',
75+
buttons: ['Cancel', 'Yes', 'No'],
76+
defaultId: 0,
77+
title: 'Save changes',
78+
message: 'Do you want save your changes?',
79+
});
80+
if (result === 1) { // Yes
81+
saveProjectClicked();
82+
} else if (result === 2) { // No
83+
return true;
84+
}
85+
}
86+
return !projectMeta.changed;
87+
}
88+
89+
function saveAsClicked() {
90+
const file = saveProjectRequest(mainWindow);
91+
if (file.length > 0) {
92+
projectMeta.file = file;
93+
projectMeta.changed = false;
94+
mainWindow.setTitle(`${path.basename(file)} - Rapid Power Estimator`);
95+
}
96+
}
97+
98+
function newProjectClicked() {
99+
if (projectSaved()) {
100+
projectMeta = {
101+
file: '', notes: '', lang: '0', name: '', changed: false,
102+
};
103+
mainWindow.setTitle(`${untitled} - Rapid Power Estimator`);
104+
sendProjectDataToRenderer();
105+
}
106+
}
107+
108+
function openProjectClicked() {
109+
if (projectSaved()) {
110+
const projectFile = openProjectRequest(mainWindow);
111+
if (projectFile.length > 0) {
112+
projectMeta.file = projectFile;
113+
fetchProjectData(projectMeta, (data) => {
114+
projectMeta = data;
115+
mainWindow.setTitle(`${path.basename(projectMeta.file)} - Rapid Power Estimator`);
116+
sendProjectDataToRenderer();
117+
});
118+
}
119+
}
120+
}
121+
39122
const isDev = process.argv.find((val) => val === '--development');
40123
if (!isDev) {
41124
['log', 'warn', 'error', 'info', 'debug'].forEach((method) => {
@@ -47,12 +130,23 @@ const template = [
47130
{
48131
label: 'File',
49132
submenu: [
50-
{ label: 'New Project' },
51-
{ label: 'Open Project' },
52-
{ label: 'Close Project' },
133+
{
134+
label: 'New Project',
135+
click: () => { newProjectClicked(); },
136+
},
137+
{
138+
label: 'Open Project',
139+
click: () => { openProjectClicked(); },
140+
},
53141
{ label: 'Sample Project' },
54-
{ label: 'Save' },
55-
{ label: 'Save as...' },
142+
{
143+
label: 'Save',
144+
click: () => { saveProjectClicked(); },
145+
},
146+
{
147+
label: 'Save as...',
148+
click: () => { saveAsClicked(); },
149+
},
56150
{ type: 'separator' },
57151
{
58152
label: 'Preferences',
@@ -144,6 +238,7 @@ const createWindow = () => {
144238
nodeIntegration: true,
145239
contextIsolation: true,
146240
},
241+
title: `${untitled} - Rapid Power Estimator`,
147242
});
148243
const indexPath = path.join(app.getAppPath(), 'build/index.html');
149244
mainWindow.loadURL(`file://${indexPath}`);
@@ -168,6 +263,12 @@ const createWindow = () => {
168263
ipcMain.on('getConfig', (event, arg) => {
169264
mainWindow.webContents.send('loadConfig', store.store);
170265
});
266+
ipcMain.on('projectData', (event, arg) => {
267+
projectMeta.notes = arg.notes;
268+
projectMeta.lang = arg.lang;
269+
projectMeta.name = arg.name;
270+
projectMeta.changed = true;
271+
});
171272
};
172273

173274
app.whenReady().then(() => {

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
"rpe.config.json",
2424
"node_modules/**/*",
2525
"package.json",
26-
"cleanup.js"
26+
"cleanup.js",
27+
"projectFile.js"
2728
],
2829
"directories": {
2930
"buildResources": "resources"

preload.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const { contextBridge, ipcRenderer } = require('electron');
22

33
contextBridge.exposeInMainWorld('ipcAPI', {
4-
loadPreferences: (channel, listener) => ipcRenderer.on(channel, listener),
4+
ipcRendererOn: (channel, listener) => ipcRenderer.on(channel, listener),
55
send: (channel, data) => ipcRenderer.send(channel, data),
66
});

projectFile.js

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const { dialog } = require('electron');
2+
3+
function openProjectRequest(parent) {
4+
const result = dialog.showOpenDialogSync(parent, {
5+
properties: ['openFile'],
6+
title: 'Open project file...',
7+
filters: [{ name: 'Project file (*.rpe)', extensions: ['rpe'] }],
8+
});
9+
if (result !== undefined) {
10+
return result[0];
11+
}
12+
return '';
13+
}
14+
15+
function saveProjectRequest(parent) {
16+
const result = dialog.showSaveDialogSync(parent, {
17+
properties: ['showOverwriteConfirmation'],
18+
title: 'Save project file...',
19+
filters: [{ name: 'Project file (*.rpe)', extensions: ['rpe'] }],
20+
});
21+
return result === undefined ? '' : result;
22+
}
23+
24+
function sendProjectData(projectData) {
25+
// TODO pending API
26+
}
27+
28+
function fetchProjectData(projectData, callback) {
29+
// TODO pending API
30+
callback({ // TODO, replace with real data
31+
file: projectData.file, lang: '1', name: 'Name', notes: 'some notes', changed: false,
32+
});
33+
}
34+
35+
module.exports = {
36+
openProjectRequest,
37+
saveProjectRequest,
38+
sendProjectData,
39+
fetchProjectData,
40+
};

src/App.js

+30-9
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,11 @@ function App() {
4141
const [mode, setMode] = React.useState(false);
4242
const [autoSave, setAutoSave] = React.useState(false);
4343
const [modalOpen, setModalOpen] = React.useState(false);
44-
const [notes, setNotes] = React.useState('');
45-
const [topLevel, setTopLevel] = React.useState('');
44+
const [projectData, setProjectData] = React.useState({
45+
name: '',
46+
lang: 0,
47+
notes: '',
48+
});
4649
const [config, setConfig] = React.useState({
4750
useDefaultFile: true,
4851
device_xml: '',
@@ -77,6 +80,10 @@ function App() {
7780
return Object.keys(object).find((key) => object[key] === value);
7881
}
7982

83+
function sendProjectData(projectDataValue) {
84+
window.ipcAPI.send('projectData', projectDataValue);
85+
}
86+
8087
React.useEffect(() => {
8188
const key = getKeyByValue(Table, openedTable);
8289
toggleItemSelection(key);
@@ -86,14 +93,17 @@ function App() {
8693
React.useEffect(() => {
8794
window.ipcAPI.send('getConfig');
8895
if ((typeof window !== 'undefined')) {
89-
window.ipcAPI.loadPreferences('preferences', (event, data) => {
96+
window.ipcAPI.ipcRendererOn('preferences', (event, data) => {
9097
setConfig(data);
9198
showModal();
9299
});
93-
window.ipcAPI.loadPreferences('loadConfig', (event, data) => {
100+
window.ipcAPI.ipcRendererOn('loadConfig', (event, data) => {
94101
setConfig(data);
95102
server.setPort(data.port, setDevices);
96103
});
104+
window.ipcAPI.ipcRendererOn('projectData', (event, data) => {
105+
setProjectData({ notes: data.notes, lang: parseInt(data.lang, 10), name: data.name });
106+
});
97107
}
98108
}, []);
99109

@@ -113,11 +123,22 @@ function App() {
113123
};
114124

115125
const handleNotesChange = (data) => {
116-
setNotes(data);
126+
const newData = { ...projectData, notes: data };
127+
setProjectData(newData);
128+
sendProjectData(newData);
117129
};
118130

119131
// eslint-disable-next-line no-unused-vars
120132
const handleLangChange = (val) => {
133+
const newData = { ...projectData, lang: parseInt(val.target.value, 10) };
134+
setProjectData(newData);
135+
sendProjectData(newData);
136+
};
137+
138+
const handleTopNameChange = (val) => {
139+
const newData = { ...projectData, name: val.target.value };
140+
setProjectData(newData);
141+
sendProjectData(newData);
121142
};
122143

123144
const handleConfigChange = (name, val) => {
@@ -161,9 +182,9 @@ function App() {
161182
<div className="last-time">{time}</div>
162183
<div className="save-icon" onClick={() => setTime(moment().format(timeFormat))}><FiSave /></div>
163184
</div>
164-
<input type="text" placeholder="Top level name" value={topLevel} onChange={(e) => setTopLevel(e.target.value)} />
165-
<select value={0} onChange={handleLangChange}>
166-
<option value={0} disabled>HDL lang</option>
185+
<input type="text" placeholder="Top level name" value={projectData.name} onChange={handleTopNameChange} />
186+
<select value={projectData.lang} onChange={handleLangChange}>
187+
<option value={0}>HDL lang</option>
167188
<option value={1}>Verilog</option>
168189
<option value={2}>HDL</option>
169190
</select>
@@ -256,7 +277,7 @@ function App() {
256277
}
257278
{modalOpen && (
258279
<Notes
259-
defaultValue={notes}
280+
defaultValue={projectData.notes}
260281
closeModal={() => {
261282
setModalOpen(false);
262283
}}

src/index.html

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
http-equiv="Content-Security-Policy"
88
content="default-src 'self'; script-src 'self'"
99
/-->
10-
<title>Rapid Power Estimator</title>
1110
<link rel="stylesheet" href="../src/style.css" type="text/css" />
1211
</head>
1312
<body>

0 commit comments

Comments
 (0)