on master help sign in
Revert "Get rid of Selection, simplifying the field API"

This reverts commit 8e2c6989b4ae8ef4e8a27478ccb840d757fc2785.

commitcbf7a8633b301fe36de6c4281a9a6351988595cf authorEvan Czaplicki <info@elm-lang.org> dateMon 3 Mar 2014, 00:12 (13 years ago) parent8e2c698
2 files changed+96−10
libraries/Graphics/Input/Field.elm+36−6 libraries/Native/Graphics/Input.js+60−4
libraries/Graphics/Input/Field.elm+36−6
@@ -6,6 +6,9 @@ approach as the [`Graphics.Input`](Graphics-Input) module for describing an
66# Create Fields
77@docs field, password, email
88
9+# Field Content
10+@docs Content, Selection, Direction, noContent
11+
912# Field Style
1013@docs Style, Outline, noOutline, Highlight, noHighlight, Dimensions, uniformly
1114-}
@@ -72,29 +75,56 @@ defaultStyle =
7275 , style = Text.defaultStyle
7376 }
7477
78+{-| Represents the current content of a text field. For example:
79+
80+ Content "She sells sea shells" (Selection 0 3 Backward)
81+
82+This means the user highlighted the substring `"She"` backwards.
83+-}
84+type Content = { string:String, selection:Selection }
85+
86+{-| The selection within a text field. `start` is never greater than `end`:
87+
88+ Selection 0 0 Forward -- cursor precedes all characters
89+
90+ Selection 5 9 Backward -- highlighting characters starting after
91+ -- the 5th and ending after the 9th
92+-}
93+type Selection = { start:Int, end:Int, direction:Direction }
94+
95+{-| The direction of selection.-}
96+data Direction = Forward | Backward
97+
98+{-| A field with no content:
99+
100+ Content "" (Selection 0 0 Forward)
101+-}
102+noContent : Content
103+noContent = Content "" (Selection 0 0 Forward)
104+
75105{-| Create a text field. The following example creates a time-varying element
76106called `nameField`. As the user types their name, the field will be updated
77107to match what they have entered.
78108
79− name : Input String
80− name = input ""
109+ name : Input Content
110+ name = input noContent
81111
82112 nameField : Signal Element
83113 nameField = field name.handle id defaultStyle "Name" <~ name.signal
84114-}
85−field : Handle a -> (String -> a) -> Style -> String -> String -> Element
115+field : Handle a -> (Content -> a) -> Style -> String -> Content -> Element
86116field = Native.Graphics.Input.field
87117
88118{-| Same as `field` but the UI element blocks out each characters. -}
89−password : Handle a -> (String -> a) -> Style -> String -> String -> Element
119+password : Handle a -> (Content -> a) -> Style -> String -> Content -> Element
90120password = Native.Graphics.Input.password
91121
92122{-| Same as `field` but it adds an annotation that this field is for email
93123addresses. This is helpful for auto-complete and for mobile users who may
94124get a custom keyboard with an `@` and `.com` button.
95125-}
96−email : Handle a -> (String -> a) -> Style -> String -> String -> Element
126+email : Handle a -> (Content -> a) -> Style -> String -> Content -> Element
97127email = Native.Graphics.Input.email
98128
99−-- area : Handle a -> (String -> a) -> Handle b -> ((Int,Int) -> b) -> (Int,Int) -> String -> String -> Element
129+-- area : Handle a -> (Content -> a) -> Handle b -> ((Int,Int) -> b) -> (Int,Int) -> String -> Content -> Element
100130-- area = Native.Graphics.Input.area
libraries/Native/Graphics/Input.js+60−4
@@ -209,6 +209,14 @@ Elm.Native.Graphics.Input.make = function(elm) {
209209 });
210210 }
211211
212+ function setRange(node, start, end, dir) {
213+ if (node.parentNode) {
214+ node.setSelectionRange(start, end, dir);
215+ } else {
216+ setTimeout(function(){node.setSelectionRange(start, end, dir);}, 0);
217+ }
218+ }
219+
212220 function updateIfNeeded(css, attribute, latestAttribute) {
213221 if (css[attribute] !== latestAttribute) {
214222 css[attribute] = latestAttribute;
@@ -259,7 +267,10 @@ Elm.Native.Graphics.Input.make = function(elm) {
259267
260268 field.type = model.type;
261269 field.placeholder = JS.fromString(model.placeHolder);
262− field.value = JS.fromString(model.content);
270+ field.value = JS.fromString(model.content.string);
271+ var selection = model.content.selection;
272+ var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
273+ setRange(field, selection.start, selection.end, direction);
263274
264275 field.elm_signal = model.signal;
265276 field.elm_handler = model.handler;
@@ -271,7 +282,15 @@ Elm.Native.Graphics.Input.make = function(elm) {
271282 String.fromCharCode(event.keyCode) +
272283 curr.slice(field.selectionEnd));
273284 var pos = field.selectionEnd + 1;
274− elm.notify(field.elm_signal.id, field.elm_handler(JS.toString(next)));
285+ elm.notify(field.elm_signal.id, field.elm_handler({
286+ _:{},
287+ string: JS.toString(next),
288+ selection: {
289+ start: pos,
290+ end: pos,
291+ direction: { ctor:'Forward' }
292+ },
293+ }));
275294 event.preventDefault();
276295 }
277296
@@ -281,12 +300,46 @@ Elm.Native.Graphics.Input.make = function(elm) {
281300 if (curr === next) {
282301 return;
283302 }
303+
304+ var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
305+ var start = field.selectionStart;
306+ var end = field.selectionEnd;
284307 field.value = field.elm_old_value;
285− elm.notify(field.elm_signal.id, field.elm_handler(JS.toString(next)));
308+
309+ elm.notify(field.elm_signal.id, field.elm_handler({
310+ _:{},
311+ string: JS.toString(next),
312+ selection: {
313+ start: start,
314+ end: end,
315+ direction: { ctor: direction }
316+ },
317+ }));
286318 }
287319
320+ function mouseUpdate(event) {
321+ var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
322+ elm.notify(field.elm_signal.id, field.elm_handler({
323+ _:{},
324+ string: field.value,
325+ selection: {
326+ start: field.selectionStart,
327+ end: field.selectionEnd,
328+ direction: { ctor: direction }
329+ },
330+ }));
331+ }
332+ function mousedown(event) {
333+ mouseUpdate(event);
334+ elm.node.addEventListener('mouseup', mouseup);
335+ }
336+ function mouseup(event) {
337+ mouseUpdate(event);
338+ elm.node.removeEventListener('mouseup', mouseup)
339+ }
288340 field.addEventListener('keypress', keyUpdate);
289341 field.addEventListener('input', inputUpdate);
342+ field.addEventListener('mousedown', mousedown);
290343
291344 return field;
292345 }
@@ -300,9 +353,12 @@ Elm.Native.Graphics.Input.make = function(elm) {
300353
301354 field.type = newModel.type;
302355 field.placeholder = JS.fromString(newModel.placeHolder);
303− var value = JS.fromString(newModel.content);
356+ var value = JS.fromString(newModel.content.string);
304357 field.value = value;
305358 field.elm_old_value = value;
359+ var selection = newModel.content.selection;
360+ var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
361+ setRange(field, selection.start, selection.end, direction);
306362 }
307363
308364 function mkField(type) {