diff --git a/.eslintrc.yml b/.eslintrc.yml
index 96383c1da..b19c5391f 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -35,7 +35,8 @@ globals:
AppController: false
MSImageData: false
NSImage: false
- MSModalInputSheet: false
+ NSTextField: false
+ NSSlider: false
MSStyleFill: false
MSStyleBorder: false
MSColor: false
diff --git a/CHANGELOG.json b/CHANGELOG.json
index 7080b1f10..a3302508d 100644
--- a/CHANGELOG.json
+++ b/CHANGELOG.json
@@ -1,5 +1,7 @@
{
- "unreleased": [],
+ "unreleased": [
+ "[New] Add UI.getInputFromUser method and deprecate the other input methods"
+ ],
"releases": {
"52.1": ["[New] Add basic support for Shape path"],
"52": [
diff --git a/Source/ui/UI.js b/Source/ui/UI.js
index 9bcdf516e..d981f9f41 100644
--- a/Source/ui/UI.js
+++ b/Source/ui/UI.js
@@ -1,5 +1,5 @@
/* globals NSAlertFirstButtonReturn */
-
+import util from 'util'
import { isNativeObject } from '../dom/utils'
function getPluginAlertIcon() {
@@ -48,6 +48,146 @@ export function alert(title, text) {
return dialog.runModal()
}
+export const INPUT_TYPE = {
+ string: 'string',
+ slider: 'slider',
+ selection: 'selection',
+ // coming soon
+ // number: 'number',
+ // color: 'color',
+ // path: 'path'
+}
+
+export function getInputFromUser(messageText, options, callback) {
+ /* eslint-disable no-param-reassign */
+ if (!options) {
+ options = {}
+ callback = () => {}
+ } else if (util.isFunction(options)) {
+ callback = options
+ options = {}
+ }
+ /* eslint-enable */
+
+ const type = String(options.type || INPUT_TYPE.string)
+
+ if (options.type && !INPUT_TYPE[type]) {
+ throw new Error('unknown input type')
+ }
+ if (!messageText || typeof messageText !== 'string') {
+ throw new Error('input description missing')
+ }
+
+ let accessory
+ switch (type) {
+ case INPUT_TYPE.string:
+ accessory = NSTextField.alloc().initWithFrame(NSMakeRect(0, 0, 200, 25))
+ accessory.setStringValue(
+ String(
+ typeof options.initialValue === 'undefined'
+ ? ''
+ : options.initialValue
+ )
+ )
+ break
+ // case INPUT_TYPE.number:
+ // accessory = NSStepper.alloc().initWithFrame(NSMakeRect(0, 0, 200, 25))
+ // accessory.setFloatValue(Number(options.initialValue || 0))
+ // if (typeof options.maxValue !== 'undefined') {
+ // accessory.setMaxValue(options.maxValue)
+ // }
+ // if (typeof options.minValue !== 'undefined') {
+ // accessory.setMinValue(options.minValue)
+ // }
+ // if (typeof options.increment !== 'undefined') {
+ // accessory.setIncrement(options.increment)
+ // }
+ // break
+ case INPUT_TYPE.slider: {
+ accessory = NSSlider.alloc().initWithFrame(NSMakeRect(0, 0, 200, 25))
+ accessory.setFloatValue(Number(options.initialValue || 0))
+ if (typeof options.maxValue !== 'undefined') {
+ accessory.setMaxValue(options.maxValue)
+ }
+ if (typeof options.minValue !== 'undefined') {
+ accessory.setMinValue(options.minValue)
+ }
+ if (typeof options.increment !== 'undefined') {
+ accessory.setAllowsTickMarkValuesOnly(true)
+ accessory.setNumberOfTickMarks(
+ parseInt(
+ 1 +
+ ((typeof options.maxValue !== 'undefined'
+ ? options.maxValue
+ : 1) -
+ (typeof options.minValue !== 'undefined'
+ ? options.minValue
+ : 0)) /
+ options.increment,
+ 10
+ )
+ )
+ }
+ break
+ }
+ case INPUT_TYPE.selection: {
+ if (!util.isArray(options.possibleValues)) {
+ throw new Error(
+ 'When the input type is `selection`, you need to provide the array of possible choices.'
+ )
+ }
+ accessory = NSComboBox.alloc().initWithFrame(NSMakeRect(0, 0, 200, 25))
+ accessory.addItemsWithObjectValues(options.possibleValues)
+ const initialIndex = options.possibleValues.indexOf(options.initialValue)
+ accessory.selectItemAtIndex(initialIndex !== -1 ? initialIndex : 0)
+ accessory.editable = false
+ break
+ }
+ default:
+ break
+ }
+
+ const dialog = NSAlert.alloc().init()
+ dialog.setMessageText(messageText)
+ if (options.description) {
+ dialog.setInformativeText(options.description)
+ }
+ dialog.addButtonWithTitle(options.okButton || 'OK')
+ dialog.addButtonWithTitle(options.cancelButton || 'Cancel')
+ dialog.setAccessoryView(accessory)
+ dialog.icon = getPluginAlertIcon()
+
+ const responseCode = dialog.runModal()
+
+ if (responseCode !== NSAlertFirstButtonReturn) {
+ callback(new Error('user canceled input'))
+ return
+ }
+
+ switch (type) {
+ case INPUT_TYPE.string:
+ callback(null, String(accessory.stringValue()))
+ return
+ // case INPUT_TYPE.number:
+ // return Number(accessory.stringValue())
+ case INPUT_TYPE.slider: {
+ const value =
+ typeof options.increment !== 'undefined'
+ ? accessory.closestTickMarkValueToValue(accessory.floatValue())
+ : accessory.floatValue()
+ callback(null, Number(value))
+ return
+ }
+ case INPUT_TYPE.selection: {
+ const selectedIndex = accessory.indexOfSelectedItem()
+ callback(null, options.possibleValues[selectedIndex])
+ return
+ }
+ default:
+ callback(null, undefined)
+ }
+}
+
/**
* Shows a simple input sheet which displays a message, and asks for a single string
* input.
@@ -56,14 +196,27 @@ export function alert(title, text) {
* @return The string that the user input.
*/
export function getStringFromUser(msg, initial) {
- const panel = MSModalInputSheet.alloc().init()
- const result = panel.runPanelWithNibName_ofType_initialString_label_(
- 'MSModalInputSheet',
- 0,
- String(typeof initial === 'undefined' ? '' : initial),
- msg
+ console.warn(
+ `\`UI.getStringFromUser(message, initialValue)\` is deprecated.
+Use \`UI.getInputFromUser(
+ message,
+ { initialValue },
+ (error, value) => {}
+)\` instead.`
)
- return String(result)
+ const accessory = NSTextField.alloc().initWithFrame(NSMakeRect(0, 0, 200, 25))
+ accessory.setStringValue(
+ String(typeof initial === 'undefined' ? '' : initial)
+ )
+ const dialog = NSAlert.alloc().init()
+ dialog.setMessageText('User input')
+ dialog.setInformativeText(msg)
+ dialog.addButtonWithTitle('OK')
+ dialog.addButtonWithTitle('Cancel')
+ dialog.setAccessoryView(accessory)
+ dialog.icon = getPluginAlertIcon()
+ dialog.runModal()
+ return String(accessory.stringValue())
}
/**
@@ -76,6 +229,14 @@ export function getStringFromUser(msg, initial) {
* @return An array with three items: [responseCode, selection, ok].
*/
export function getSelectionFromUser(msg, items, selectedItemIndex = 0) {
+ console.warn(
+ `\`UI.getSelectionFromUser(message, items, selectedItemIndex)\` is deprecated.
+Use \`UI.getInputFromUser(
+ message,
+ { type: UI.INPUT_TYPE.selection, items, initialValue },
+ (error, value) => {}
+)\` instead.`
+ )
const accessory = NSComboBox.alloc().initWithFrame(NSMakeRect(0, 0, 200, 25))
accessory.addItemsWithObjectValues(items)
accessory.selectItemAtIndex(selectedItemIndex)
diff --git a/docs/api/UI.md b/docs/api/UI.md
index 2dfb57949..488b1dd76 100644
--- a/docs/api/UI.md
+++ b/docs/api/UI.md
@@ -36,47 +36,61 @@ Show an alert with a custom title and message. The alert is modal, so it will st
| titlestring - required | The title of the alert. |
| textstring - required | The text of the message. |
-## Get a string input from the user
-
-```js
-var string = UI.getStringFromUser("What's your name?", 'Appleseed')
+## Get an input from the user
+
+```javascript
+UI.getInputFromUser(
+ "What's your name?",
+ {
+ initialValue: 'Appleseed',
+ },
+ (err, value) => {
+ if (err) {
+ // most likely the user canceled the input
+ return
+ }
+ }
+)
```
-Shows a simple input sheet which displays a message, and asks for a single string input.
-
-| Parameters | |
-| ------------------------------------------------------ | -------------------------------------- |
-| messagestring - required | The prompt message to show. |
-| initialValuestring | The initial value of the input string. |
-
-### Returns
-
-The string that the user input, or "null" (String) if the user clicked 'Cancel'.
-
-## Make the user select an option
+```javascript
+UI.getInputFromUser("What's your favorite design tool?", {
+ type: UI.INPUT_TYPE.selection
+ possibleValues: ['Sketch']
+}, (err, value) => {
+ if (err) {
+ // most likely the user canceled the input
+ return
+ }
+})
+```
-```js
-var options = ['Sketch']
-var selection = UI.getSelectionFromUser(
- "What's your favorite design tool?",
- options
+```javascript
+UI.getInputFromUser(
+ "What's the opacity of the new layer?",
+ {
+ type: UI.INPUT_TYPE.slider,
+ },
+ (err, value) => {
+ if (err) {
+ // most likely the user canceled the input
+ return
+ }
+ }
)
-
-var ok = selection[2]
-var value = options[selection[1]]
-if (ok) {
- // do something
-}
```
-Shows an input sheet which displays a popup with a series of options, from which the user is asked to choose.
-
-| Parameters | |
-| ------------------------------------------------------ | ------------------------------------------ |
-| messagestring - required | The prompt message to show. |
-| itemsstring[] - required | An array of option items. |
-| selectedIndexnumber | The index of the item to select initially. |
-
-### Returns
-
-An array with a response code, the selected index and `ok`. The code will be one of `NSAlertFirstButtonReturn` or `NSAlertSecondButtonReturn`. The selection will be the integer index of the selected item. `ok` is the boolean `code === NSAlertFirstButtonReturn`.
+Shows a simple input sheet which displays a message, and asks for an input from the user.
+
+| Parameters | |
+| --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| messagestring - required | The prompt message to show. |
+| optionsobject | Options to customize the input sheet. Most of the options depends on the type of the input. |
+| option.descriptionstring | A secondary text to describe with more details the input. |
+| option.type[Input Type](#inputtype) | The type of the input. |
+| option.initialValuestring | number | The initial value of the input. |
+| option.possibleValuesstring[] - required with a selection | The possible choices that the user can make. Only used for a `selection` input. |
+| option.maxValuenumber | The maximal value. Only used for a `slider` input. Defaults to `1`. |
+| option.minValuenumber | The maximal value. Only used for a `slider` input. Defaults to `0`. |
+| option.incrementnumber | Restricts the possible values to multiple of the increment. Only used for a `slider` input. |
+| callbackfunction | A function called after the user entered the input. It is called with an `Error` if the user canceled the input and a `string` or `number` depending on the input type (or `undefined`). |