Skip to content

Commit 5dc1179

Browse files
committed
implement keybind prompt
1 parent eae4cca commit 5dc1179

File tree

6 files changed

+578
-73
lines changed

6 files changed

+578
-73
lines changed

providers/prompt/dark-prompt.css

+26-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ body {
1818
overflow: hidden;
1919
}
2020

21-
#data {
21+
#data,
22+
.keybindData {
2223
background: unset;
2324
color: whitesmoke;
2425
border: 1px solid rgb(54, 54, 54);
@@ -41,12 +42,35 @@ body {
4142
}
4243

4344
#ok,
44-
#cancel {
45+
#cancel,
46+
.clearButton {
4547
background-color: rgb(0, 0, 0);
4648
color: whitesmoke;
4749
}
4850

51+
/* For Counter Prompt */
4952
.minus,
5053
.plus {
5154
background: rgb(0, 0, 0);
5255
}
56+
57+
/* For Select Prompt */
58+
option {
59+
background-color: #07070C;
60+
}
61+
62+
/* For Keybind Prompt */
63+
.clearButton:focus {
64+
outline: none;
65+
}
66+
.clearButton:hover {
67+
background-color: rgb(5, 5, 5);
68+
}
69+
.keybindData:hover {
70+
border: 1px solid rgb(56, 0, 0);
71+
}
72+
73+
.keybindData:focus {
74+
outline: 3px solid #1E0919;
75+
border: 1px solid rgb(56, 0, 0);
76+
}

providers/prompt/index.js

+20-14
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ const url = require("url");
66
const path = require("path");
77

88
const DEFAULT_WIDTH = 370;
9+
const DEFAULT_KEYBIND_WIDTH = 420;
910
const DEFAULT_COUNTER_WIDTH = 300;
10-
const DEFAULT_HEIGHT = 160;
11-
const DEFAULT_COUNTER_HEIGHT = 150;
11+
const DEFAULT_HEIGHT = 150;
12+
const DEFAULT_KEYBIND_HEIGHT = options => (options.length * 40) + 100;
1213

1314
function electronPrompt(options, parentWindow) {
1415
return new Promise((resolve, reject) => {
@@ -18,8 +19,8 @@ function electronPrompt(options, parentWindow) {
1819
//custom options override default
1920
const options_ = Object.assign(
2021
{
21-
width: options?.type === "counter" ? DEFAULT_COUNTER_WIDTH : DEFAULT_WIDTH,
22-
height: options?.type === "counter" ? DEFAULT_COUNTER_HEIGHT : DEFAULT_HEIGHT,
22+
width: options?.type === "counter" ? DEFAULT_COUNTER_WIDTH : options?.type === "keybind" ? DEFAULT_KEYBIND_WIDTH : DEFAULT_WIDTH,
23+
height: options?.type === "keybind" && options?.keybindOptions ? DEFAULT_KEYBIND_HEIGHT(options.keybindOptions) : DEFAULT_HEIGHT,
2324
resizable: false,
2425
title: "Prompt",
2526
label: "Please input a value:",
@@ -28,6 +29,7 @@ function electronPrompt(options, parentWindow) {
2829
value: null,
2930
type: "input",
3031
selectOptions: null,
32+
keybindOptions: null,
3133
counterOptions: { minimum: null, maximum: null, multiFire: false },
3234
icon: null,
3335
useHtmlLabel: false,
@@ -41,22 +43,21 @@ function electronPrompt(options, parentWindow) {
4143
options || {}
4244
);
4345

44-
options_.minWidth = options?.minWidth || options?.width || options_.width;
45-
options_.minHeight = options?.minHeight || options?.height || options_.height;
46+
4647

4748
if (options_.customStylesheet === "dark") {
4849
options_.customStylesheet = require("path").join(__dirname, "dark-prompt.css");
4950
}
5051

51-
if (options_.type === "counter" && (options_.counterOptions !== null && typeof options_.selectOptions !== "object")) {
52-
reject(new Error('"counterOptions" must be an object if specified'));
53-
return;
52+
for (let type of ["counter", "select", "keybind"]) {
53+
if (options_.type === type && (!options_[`${type}Options`] || typeof options_[`${type}Options`] !== "object")) {
54+
reject(new Error(`"${type}Options" must be an object if type = ${type}`));
55+
return;
56+
}
5457
}
5558

56-
if (options_.type === "select" && (options_.selectOptions === null || typeof options_.selectOptions !== "object")) {
57-
reject(new Error('"selectOptions" must be an object'));
58-
return;
59-
}
59+
options_.minWidth = options?.minWidth || options?.width || options_.width;
60+
options_.minHeight = options?.minHeight || options?.height || options_.height;
6061

6162
let promptWindow = new BrowserWindow({
6263
frame: options_.frame,
@@ -104,6 +105,11 @@ function electronPrompt(options, parentWindow) {
104105

105106
//get input from front
106107
const postDataListener = (event, value) => {
108+
if (options_.type === "keybind" && value) {
109+
for (let i=0; i < value.length ;i++) {
110+
value[i] = JSON.parse(value[i])
111+
}
112+
}
107113
resolve(value);
108114
event.returnValue = null;
109115
cleanup();
@@ -135,7 +141,7 @@ function electronPrompt(options, parentWindow) {
135141

136142
//should never happen
137143
promptWindow.webContents.on("did-fail-load", (
138-
event,
144+
_event,
139145
errorCode,
140146
errorDescription,
141147
validatedURL

providers/prompt/page/counter.js

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
const { promptCreateInput } = require("./prompt");
2+
3+
module.exports = { promptCreateCounter , validateCounterInput }
4+
5+
let options;
6+
7+
8+
function promptCreateCounter(promptOptions, parentElement) {
9+
options = promptOptions;
10+
if (options.counterOptions?.multiFire) {
11+
document.onmouseup = () => {
12+
if (nextTimeoutID) {
13+
clearTimeout(nextTimeoutID)
14+
nextTimeoutID = null;
15+
}
16+
};
17+
}
18+
19+
options.value = validateCounterInput(options.value);
20+
21+
const dataElement = promptCreateInput();
22+
dataElement.onkeypress = function isNumberKey(e) {
23+
if (Number.isNaN(parseInt(e.key)) && e.key !== "Backspace" && e.key !== "Delete")
24+
return false;
25+
return true;
26+
}
27+
28+
dataElement.style.width = "unset";
29+
dataElement.style["text-align"] = "center";
30+
31+
parentElement.append(createMinusButton(dataElement));
32+
parentElement.append(dataElement);
33+
parentElement.append(createPlusButton(dataElement));
34+
35+
return dataElement;
36+
}
37+
38+
let nextTimeoutID = null;
39+
40+
/** Function execute callback in 3 accelerated intervals based on timer.
41+
* Terminated from document.onmouseup() that is registered from promptCreateCounter()
42+
* @param {function} callback function to execute
43+
* @param {object} timer {
44+
* * time: First delay in miliseconds
45+
* * scaleSpeed: Speed change per tick on first acceleration
46+
* * limit: First Speed Limit, gets divided by 2 after $20 calls. $number change exponentially
47+
* }
48+
* @param {int} stepArgs argument for callback representing Initial steps per click, default to 1
49+
* steps starts to increase when speed is too fast to notice
50+
* @param {int} counter used internally to decrease timer.limit
51+
*/
52+
function multiFire(callback, timer = { time: 300, scaleSpeed: 100, limit: 100 }, stepsArg = 1, counter = 0) {
53+
callback(stepsArg);
54+
55+
const nextTimeout = timer.time;
56+
57+
if (counter > 20) {
58+
counter = 0 - stepsArg;
59+
if (timer.limit > 1) {
60+
timer.limit /= 2;
61+
} else {
62+
stepsArg *= 2;
63+
}
64+
}
65+
66+
if (timer.time !== timer.limit) {
67+
timer.time = Math.max(timer.time - timer.scaleSpeed, timer.limit)
68+
}
69+
70+
nextTimeoutID = setTimeout(
71+
multiFire, //callback
72+
nextTimeout, //timer
73+
//multiFire args:
74+
callback,
75+
timer,
76+
stepsArg,
77+
counter + 1
78+
);
79+
}
80+
81+
function createMinusButton(dataElement) {
82+
function doMinus(steps) {
83+
dataElement.value = validateCounterInput(parseInt(dataElement.value) - steps);
84+
}
85+
86+
const minusBtn = document.createElement("span");
87+
minusBtn.textContent = "-";
88+
minusBtn.classList.add("minus");
89+
90+
if (options.counterOptions?.multiFire) {
91+
minusBtn.onmousedown = () => {
92+
multiFire(doMinus);
93+
};
94+
} else {
95+
minusBtn.onmousedown = () => {
96+
doMinus();
97+
};
98+
}
99+
100+
return minusBtn;
101+
}
102+
103+
function createPlusButton(dataElement) {
104+
function doPlus(steps) {
105+
dataElement.value = validateCounterInput(parseInt(dataElement.value) + steps);
106+
}
107+
108+
const plusBtn = document.createElement("span");
109+
plusBtn.textContent = "+";
110+
plusBtn.classList.add("plus");
111+
112+
if (options.counterOptions?.multiFire) {
113+
plusBtn.onmousedown = () => {
114+
multiFire(doPlus);
115+
};
116+
} else {
117+
plusBtn.onmousedown = () => {
118+
doPlus();
119+
};
120+
}
121+
122+
return plusBtn;
123+
}
124+
125+
//validate counter
126+
function validateCounterInput(input) {
127+
128+
const min = options.counterOptions?.minimum;
129+
const max = options.counterOptions?.maximum;
130+
//note that !min/max would proc if min/max are 0
131+
if (min !== null && min !== undefined && input < min) {
132+
return min;
133+
}
134+
135+
if (max !== null && max !== undefined && input > max) {
136+
return max;
137+
}
138+
139+
return input;
140+
}

0 commit comments

Comments
 (0)