Skip to content

Commit 9d675a2

Browse files
authored
Remember saved responses to TaskDialog (#664)
* added saved responses prf * add css script * task and string dialog updated for ZF, nix and mac * update min size of prefs window on electron * fix height of dialog win on mac
1 parent 83ce507 commit 9d675a2

14 files changed

Lines changed: 1365 additions & 1184 deletions

File tree

dialog.html

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<html>
33
<head>
44
<meta charset=utf-8>
5+
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'">
56
<title>Task</title>
67
<link rel=stylesheet href='style/ride-base.css'>
78
<link rel=stylesheet class=theme id=theme_dark href='style/dark-theme.css'>
@@ -12,14 +13,24 @@
1213
<div id=gd class="dlg floating"><!--generic dialog for processing OptionsDialog,StringDialog,TaskDialog-->
1314
<div class=dlg_content id=gd_content></div>
1415
<div class=dlg_btns id=gd_btns ></div>
16+
<div id="gd_footer">
17+
<div id="gd_footer_btns"></div>
18+
<input id=gd_footer_qn type="checkbox" name="question">
19+
<label id=gd_footer_qn_lbl for=gd_footer_qn></label>
20+
</div>
1521
</div>
1622

1723
<script>
1824
const ESC = { '<': '&lt;', '>': '&gt;', '&': '&amp;', "'": '&apos;', '"': '&quot;' };
1925
const esc = s => s.replace(/[<>&'"]/g, x => ESC[x]);
2026

2127
const ipc = require('node-ipc');
28+
const el = require('electron').remote;
29+
const bw = el.getCurrentWindow();
30+
const [width] = bw.getSize();
2231
const qp = require('querystring').parse(window.location.search.slice(1));
32+
const iswin = /^win/i.test(process.platform);
33+
2334
ipc.config.id = 'dialog';
2435
ipc.config.appspace = qp.appid;
2536
ipc.config.retry = 1500;
@@ -30,7 +41,7 @@
3041
rm.on('connect', () => {
3142
window.onbeforeunload = (e) => {
3243
e.returnValue = false;
33-
rm.emit('dialogClose', [token, task ? -1 : value]);
44+
rm.emit('dialogClose', [token, { index: task ? -1 : value }]);
3445
};
3546
rm.emit('dialogCreated');
3647
});
@@ -48,6 +59,11 @@
4859

4960
const gd_content = document.getElementById('gd_content');
5061
const gd_btns = document.getElementById('gd_btns');
62+
const gd_footer = document.getElementById('gd_footer');
63+
const gd_footer_btns = document.getElementById('gd_footer_btns');
64+
const gd_footer_qn = document.getElementById('gd_footer_qn');
65+
const gd_footer_qn_lbl = document.getElementById('gd_footer_qn_lbl');
66+
5167
let value;
5268
let task;
5369
let token;
@@ -57,36 +73,42 @@
5773
document.title = x.title || 'Dyalog';
5874
task = !!x.buttonText;
5975
value = x.defaultValue || null;
76+
gd_footer.hidden = !task;
6077
if (task) {
6178
gd_content.innerHTML = esc(x.text || '') + (x.subtext ? `<div class=task_subtext>${esc(x.subtext)}</div>` : '');
6279
let btns = (x.buttonText || []).map((y) => {
6380
const [caption, ...details] = esc(y).split('\n');
6481
return '<button class=task><div class="btn_icon"><span class="fas fa-chevron-circle-right"></span></div>' +
6582
`${caption}<br><div class="task_detail">${details.join('<br>')}</div></button>`;
6683
}).join('');
67-
btns += (x.footer ? `<div class=task_footer>${esc(x.footer)}</div>` : '');
6884
gd_btns.innerHTML = btns;
69-
const ret = (r) => {
70-
gd_btns.onclick = null;
71-
rm.emit('dialogClose', [token, r]);
85+
gd_footer_btns.innerHTML = x.options.map((y) => `<button>${y}</button>`).join('');
86+
gd_footer_qn_lbl.innerHTML = x.questionlabel;
87+
gd_footer_qn.checked = false;
88+
const ret = (index, questionkey) => {
89+
[...gd_btns.getElementsByTagName('button')].forEach(x => x.onclick = null);
90+
gd_footer_btns.onclick = null;
91+
rm.emit('dialogClose', [token, { index, questionkey }]);
7292
};
7393
const clickCb = (e) => {
74-
let t = e.currentTarget;
75-
let i = 99;
94+
let t = e.currentTarget == gd_footer_btns ? e.target : e.currentTarget;
95+
let i = e.currentTarget == gd_footer_btns ? -1 : 99;
7696
while (t) { t = t.previousSibling; i += 1; }
77-
ret(i);
97+
ret(i, gd_footer_qn.checked && x.questionkey);
7898
};
7999
[...gd_btns.getElementsByTagName('button')].forEach(x => x.onclick = clickCb);
100+
gd_footer_btns.onclick = clickCb;
101+
80102
gd_btns.querySelector('button').focus();
81103
} else {
82104
gd_content.innerText = x.text || '';
83105
gd_content.insertAdjacentHTML('beforeend', `<br><input ${x.pass ? 'type=password' : ''}>`);
84106
const inp = gd_content.querySelector('input');
85107
inp.value = x.initialValue || '';
86108
gd_btns.innerHTML = '<button>OK</button><button>Cancel</button>';
87-
const ret = (r) => {
109+
const ret = (index) => {
88110
gd_btns.onclick = null;
89-
rm.emit('dialogClose', [token, r]);
111+
rm.emit('dialogClose', [token, { index }]);
90112
};
91113
gd_btns.onclick = (e) => {
92114
if (e.target.nodeName === 'BUTTON') {
@@ -104,6 +126,11 @@
104126
};
105127
setTimeout(() => { inp.focus(); }, 1);
106128
}
129+
bw.resizable = true;
130+
setTimeout(() => {
131+
bw.setSize(width, document.body.clientHeight + (iswin ? 39 : 0));
132+
bw.resizable = false;
133+
}, 1);
107134
}
108135
</script>
109136
</body>

index.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,11 @@
231231
<div id=gd_icon></div>
232232
<div class=dlg_content id=gd_content></div>
233233
<div class=dlg_btns id=gd_btns ></div>
234+
<div id="gd_footer">
235+
<div id="gd_footer_btns"></div>
236+
<input id=gd_footer_qn type="checkbox" name="question">
237+
<label id=gd_footer_qn_lbl for=gd_footer_qn></label>
238+
</div>
234239
</div>
235240

236241
<!--IDE:language bar,session,editors,etc-->
@@ -362,6 +367,7 @@
362367
<a href=#code>General</a>
363368
<a href=#lyt class=sel>Keyboard</a>
364369
<a href=#shc>Shortcuts</a>
370+
<a href=#resp>Saved Responses</a>
365371
<a href=#col>Colours</a>
366372
<a href=#title>Title</a>
367373
<a href=#pmenu>Menu</a>
@@ -459,6 +465,15 @@
459465
<div id=shc_no_res hidden>No results</div>
460466
</div>
461467

468+
<!--Saved Responses-->
469+
<div id=resp hidden>
470+
<div id=resp_ctrls>
471+
<button id=resp_sel_all title="Select all responses"><u>S</u>elect All</button>
472+
<button id=resp_del title="Delete response"><u>D</u>elete</button>
473+
</div>
474+
<select id=resp_qns multiple></select>
475+
</div>
476+
462477
<!--Code-->
463478
<div id=code hidden>
464479
<h2>Indentation</h2>
@@ -704,6 +719,7 @@ <h2>Other</h2>
704719
<script src=src/prf_ui.js ></script>
705720
<script src=src/prf_lyt.js ></script>
706721
<script src=src/prf_shc.js ></script>
722+
<script src=src/prf_resp.js ></script>
707723
<script src=src/prf_code.js ></script>
708724
<script src=src/prf_col.js ></script>
709725
<script src=src/prf_title.js ></script>

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"start": "electron .",
99
"dev": "npm run build-css && npm run start",
1010
"styler": "electron ./utils/style-picker/style-picker.js",
11+
"css": "grunt less",
1112
"live-style": "grunt",
1213
"test": "ava -s -c=1"
1314
},

src/ide.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ D.IDE = function IDE(opts = {}) {
540540
},
541541
ReplyGetSyntaxInformation(x) {
542542
D.ParseSyntaxInformation(x);
543-
D.ipc.server && D.ipc.server.broadcast('syntax', D.syntax);
543+
D.ipc && D.ipc.server.broadcast('syntax', D.syntax);
544544
},
545545
ValueTip(x) { ide.wins[x.token].ValueTip(x); },
546546
SetHighlightLine(x) {

src/init.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ const Console = console;
9797
alwaysOnTop: false,
9898
fullscreen: false,
9999
fullscreenable: false,
100-
minWidth: 655,
100+
minWidth: 790,
101101
minHeight: 600,
102102
webPreferences: {
103103
contextIsolation: false,

src/prf.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ D.prf = {};
1515
['autocompletionDelay',500],
1616
['colourScheme', 'Default'], //name of the active colour scheme
1717
['colourSchemes', []],//objects describing user-defined colour schemes
18+
['confirmations', {}],//saved responses to TaskDialog questions
1819
['connectOnQuit', 0], // open connection page when active session ends
1920
['floating', 0], //floating editor and tracer windows
2021
['floatSingle', 1], //create single floating edit window

src/prf_resp.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
// Preferences > Shortcuts
3+
function delQns() {
4+
[...q.qns.selectedOptions].forEach(x => x.remove())
5+
return !1;
6+
}
7+
8+
let q; // DOM elements whose ids start with "resp_", keyed by the rest of the id
9+
D.prf_tabs.resp = {
10+
name: 'Saved Responses',
11+
init(t) {
12+
q = J.resp;
13+
q.del.onclick = delQns;
14+
q.sel_all.onclick = () => {
15+
[...q.qns.options].forEach(x => x.selected = true)
16+
return !1;
17+
};
18+
q.qns.onkeydown = (x) => {
19+
if (x.which == 46) return delQns(); // on del
20+
return !0;
21+
};
22+
},
23+
load() {
24+
const conf = D.prf.confirmations();
25+
q.qns.innerHTML = Object.keys(conf).map(k => `<option value=${conf[k]}>${k}`).join('');
26+
},
27+
validate() {
28+
return null;
29+
},
30+
print() {
31+
D.el.getCurrentWindow().webContents.print({ printBackground: true });
32+
},
33+
save() {
34+
const conf = {};
35+
[...q.qns.options].forEach(x => conf[x.label] = +x.value)
36+
D.prf.confirmations(conf);
37+
},
38+
activate() { q.qns.focus(); },
39+
};
40+
}

src/prf_ui.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@
112112
d.className += ' web';
113113
}
114114
}
115-
!D.el && D.util.dlg(d, { w: 655, h: 600 });
115+
!D.el && D.util.dlg(d, { w: 765, h: 600 });
116116
Object.keys(tabs).forEach((i) => { tabs[i].load(); });
117117
$(typeof tab === 'string' ? `#prf_nav a[href$=${tab}]` : '#prf_nav .sel').mousedown();
118118
};

src/util.js

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
I.gd_title_text.textContent = x.title || '';
119119
I.gd_content.innerHTML = text;
120120
I.gd_icon.style.display = 'none';
121+
I.gd_footer.hidden = true;
121122
// I.gd_icon.className = `dlg_icon_${['warn', 'info', 'query', 'error'][x.type - 1]}`;
122123
I.gd_btns.innerHTML = (x.options || []).map(y => `<button>${D.util.esc(y)}</button>`).join('');
123124
const b = I.gd_btns.querySelector('button');
@@ -154,6 +155,7 @@
154155
I.gd_title_text.textContent = x.title || '';
155156
I.gd_content.innerText = x.text || '';
156157
I.gd_icon.style.display = 'none';
158+
I.gd_footer.hidden = true;
157159
I.gd_content.insertAdjacentHTML('beforeend', '<br><input>');
158160
const inp = I.gd_content.querySelector('input');
159161
inp.value = x.initialValue || '';
@@ -191,28 +193,47 @@
191193
inp.focus();
192194
}
193195
};
194-
D.util.dlg(I.gd, { w: 400, h: 250, modal: true });
196+
D.util.dlg(I.gd, { w: 400, modal: true });
195197
setTimeout(() => { inp.focus(); }, 1);
196198
},
197199
replyDialog(t, r) {
198200
const f = dlgCb[t];
199201
if (f) {
200-
f(r);
202+
if (r.questionkey) {
203+
const conf = D.prf.confirmations();
204+
conf[r.questionkey] = r.index;
205+
D.prf.confirmations(conf);
206+
}
207+
f(r.index);
201208
delete dlgCb[t];
202209
}
203210
},
204211
taskDialog(x, f) {
212+
const conf = D.prf.confirmations();
213+
const response = conf[x.questionkey];
214+
if (response) {
215+
f(response);
216+
return;
217+
}
205218
if (D.el && D.win) {
206219
const { bwId } = D.ide.focusedWin;
207220
const bw = bwId ? D.el.BrowserWindow.fromId(bwId) : D.elw;
208-
const r = D.el.dialog.showMessageBoxSync(bw, {
221+
D.el.dialog.showMessageBox(bw, {
209222
message: `${x.text}\n${x.subtext}`,
210223
title: x.title || '',
211224
buttons: x.options.concat(x.buttonText) || [''],
225+
checkboxLabel: x.questionlabel,
226+
checkboxChecked: false,
212227
type: 'question',
228+
}).then((result) => {
229+
const r = result.response;
230+
const index = r < x.options.length ? r : 100 + (r - x.options.length);
231+
if (result.checkboxChecked) {
232+
conf[x.questionkey] = index;
233+
D.prf.confirmations(conf);
234+
}
235+
f(index);
213236
});
214-
const index = r < x.options.length ? r : 100 + (r - x.options.length);
215-
f(index);
216237
return;
217238
} else if (D.dlg_bw) {
218239
dlgCb[x.token] = f;
@@ -224,6 +245,7 @@
224245
const { esc } = D.util;
225246
I.gd_title_text.textContent = x.title || 'Task';
226247
I.gd_icon.style.display = 'none';
248+
I.gd_footer.hidden = false;
227249
I.gd_content.innerHTML = esc(x.text || '') + (x.subtext ? `<div class=task_subtext>${esc(x.subtext)}</div>` : '');
228250
let content = (x.buttonText || []).map((y) => {
229251
const [caption, ...details] = esc(y).split('\n');
@@ -232,8 +254,13 @@
232254
}).join('');
233255
content += (x.footer ? `<div class=task_footer>${esc(x.footer)}</div>` : '');
234256
I.gd_btns.innerHTML = content;
257+
I.gd_footer_btns.innerHTML = x.options.map((y) => `<button>${y}</button>`).join('');
258+
I.gd_footer_qn_lbl.innerHTML = x.questionlabel;
259+
I.gd_footer_qn.checked = false;
260+
235261
const ret = (r) => {
236262
I.gd_btns.onclick = null;
263+
I.gd_footer_btns.onclick = null;
237264
I.gd_close.onclick = null;
238265
I.gd.hidden = 1;
239266
I.dlg_modal_overlay.hidden = 1;
@@ -256,13 +283,19 @@
256283
}
257284
});
258285
I.gd_close.onclick = () => { ret(-1); };
259-
btns.on('click', (e) => {
260-
let t = e.currentTarget;
261-
let i = 99;
286+
const clickCb = (e) => {
287+
let t = e.currentTarget == gd_footer_btns ? e.target : e.currentTarget;
288+
let i = e.currentTarget == gd_footer_btns ? -1 : 99;
262289
while (t) { t = t.previousSibling; i += 1; }
290+
if (I.gd_footer_qn.checked) {
291+
conf[x.questionkey] = i;
292+
D.prf.confirmations(conf);
293+
}
263294
ret(i);
264-
});
265-
D.util.dlg(I.gd, { w: 400, h: 300, modal: true });
295+
};
296+
btns.on('click', clickCb);
297+
I.gd_footer_btns.onclick = clickCb;
298+
D.util.dlg(I.gd, { w: 400, modal: true });
266299
setTimeout(() => { fb.focus(); }, 1);
267300
},
268301
};

0 commit comments

Comments
 (0)