From 9723d2d41c833a97002ba74a1e3c9c48f9ca5156 Mon Sep 17 00:00:00 2001 From: Evan Czaplicki Date: Sat, 1 Mar 2014 17:26:19 -0500 Subject: [PATCH 01/11] Revamp the Text library so that there is an explicit Text.Style record The Style record is needed to allow formatting of text fields! --- libraries/Native/Text.js | 97 ++++++++++++++++++++++++--------------- libraries/Native/Utils.js | 7 --- libraries/Text.elm | 96 ++++++++++++++++++++++---------------- 3 files changed, 116 insertions(+), 84 deletions(-) diff --git a/libraries/Native/Text.js b/libraries/Native/Text.js index 2398586..f777a97 100644 --- a/libraries/Native/Text.js +++ b/libraries/Native/Text.js @@ -4,10 +4,9 @@ Elm.Native.Text.make = function(elm) { elm.Native.Text = elm.Native.Text || {}; if (elm.Native.Text.values) return elm.Native.Text.values; - var JS = Elm.JavaScript.make(elm); - var Utils = Elm.Native.Utils.make(elm); - var Color = Elm.Native.Color.make(elm); var Element = Elm.Graphics.Element.make(elm); + var List = Elm.Native.List.make(elm); + var Utils = Elm.Native.Utils.make(elm); var show = Elm.Native.Show.make(elm).show; function makeSpaces(s) { @@ -51,13 +50,58 @@ Elm.Native.Text.make = function(elm) { return arr.join('
'); } - function toText(str) { return Utils.txt(properEscape(JS.fromString(str))); } + function toText(str) { return Utils.txt(properEscape(str)); } + // conversions from Elm values to CSS + function toTypefaces(list) { + var typefaces = List.toArray(list); + for (var i = typefaces.length; i--; ) { + var typeface = typefaces[i]; + if (typeface.contains(' ')) { + typefaces[i] = "'" + typeface + "'"; + } + } + return typefaces.join(','); + } + function toLine(line) { + var ctor = line.ctor; + var decoration = ctor === 'Under' ? 'underline' : + ctor === 'Over' ? 'overline' : 'line-through'; + return 'text-decoration:' + decoration + ';'; + } + function toColor(c) { + var color = (c._3 === 1) + ? ('rgb(' + c._0 + ', ' + c._1 + ', ' + c._2 + ')') + : ('rgba(' + c._0 + ', ' + c._1 + ', ' + c._2 + ', ' + c._3 + ')'); + return 'color:' + color + ';'; + } + + // setting styles of Text + function style(style, text) { + var newText = '' + return Utils.txt(newText); + } function height(px, text) { return { style: 'font-size:' + px + 'px;', text:text } } - function typeface(name, text) { - return { style: 'font-family:' + name + ';', text:text } + function typeface(names, text) { + return { style: 'font-family:' + toTypefaces(names) + ';', text:text } } function monospace(text) { return { style: 'font-family:monospace;', text:text } @@ -71,14 +115,8 @@ Elm.Native.Text.make = function(elm) { function link(href, text) { return { href: toText(href), text:text }; } - function underline(text) { - return { line: ' underline', text:text }; - } - function overline(text) { - return { line: ' overline', text:text }; - } - function strikeThrough(text) { - return { line: ' line-through', text:text }; + function line(line, text) { + return { style: toLine(line), text:text }; } function color(c, text) { @@ -88,8 +126,8 @@ Elm.Native.Text.make = function(elm) { return { style: 'color:' + color + ';', text:text }; } - function position(align) { - function create(text) { + function block(align) { + return function(text) { var raw = { ctor :'RawHtml', html : Utils.makeText(text), @@ -100,7 +138,6 @@ Elm.Native.Text.make = function(elm) { var pos = A2(Utils.htmlHeight, 0, raw); return A3(Element.newElement, pos._0, pos._1, raw); } - return create; } function markdown(text, guid) { @@ -115,36 +152,22 @@ Elm.Native.Text.make = function(elm) { return A3(Element.newElement, pos._0, pos._1, raw); } - var text = position('left'); - function asText(v) { - return text(monospace(toText(show(v)))); - } - - function plainText(v) { - return text(toText(v)); - } - return elm.Native.Text.values = { toText: toText, height : F2(height), italic : italic, bold : bold, - underline : underline, - overline : overline, - strikeThrough : strikeThrough, + line : F2(line), monospace : monospace, typeface : F2(typeface), color : F2(color), link : F2(link), - justified : position('justify'), - centered : position('center'), - righted : position('right'), - text : text, - plainText : plainText, - markdown : markdown, - - asText : asText, + leftAligned : block('left'), + rightAligned : block('right'), + centered : block('center'), + justified : block('justify'), + markdown : markdown, }; }; diff --git a/libraries/Native/Utils.js b/libraries/Native/Utils.js index e59bab0..51ac6ef 100644 --- a/libraries/Native/Utils.js +++ b/libraries/Native/Utils.js @@ -76,14 +76,8 @@ Elm.Native.Utils.make = function(elm) { function makeText(text) { var style = ''; - var line = ''; var href = ''; while (true) { - if (text.line) { - line += text.line; - text = text.text; - continue; - } if (text.style) { style += text.style; text = text.text; @@ -95,7 +89,6 @@ Elm.Native.Utils.make = function(elm) { continue; } if (href) text = '' + text + ''; - if (line) style += 'text-decoration:' + line + ';'; if (style) text = '' + text + ''; return text; } diff --git a/libraries/Text.elm b/libraries/Text.elm index 37d180a..1799cb7 100644 --- a/libraries/Text.elm +++ b/libraries/Text.elm @@ -6,13 +6,21 @@ module Text where @docs toText # Creating Elements -@docs plainText, asText, text, centered, justified, righted +@docs leftAligned, rightAligned, centered, justified -# Formatting -@docs color, typeface, height, link +# Style and Links +@docs Style, style, Line, link -# Simple Formatting -@docs monospace, bold, italic, underline, overline, strikeThrough +# Convenience Functions + +There are two convenience functions for creating an `Element` which can be +useful when debugging or prototyping: + +@docs plainText, asText + +There are also a bunch of functions to set parts of a `Style` individually: + +@docs typeface, monospace, height, color, bold, italic, line -} @@ -25,75 +33,82 @@ import Native.Text data Text = Text +data Line = Under | Over | Through + +{-| Representation of all the ways you can style `Text`. +-} +type Style = + { typeface : [String] + , height : Maybe Float + , color : Color + , bold : Bool + , italic : Bool + , line : Maybe Line + } + {-| Convert a string into text which can be styled and displayed. -} toText : String -> Text toText = Native.Text.toText -{-| Set the typeface of some text. The first argument should be a comma -separated listing of the desired typefaces: - - "helvetica, arial, sans-serif" - -Works the same as the CSS font-family property. +{-| Set the style of some text. -} -typeface : String -> Text -> Text +style : Style -> Text -> Text +style = Native.Text.style + +{-| Provide a list of prefered typefaces for some text. + + ["helvetica","arial","sans-serif"] + +Not everyone has access to the same typefaces, so rendering will use the first +typeface in the list that is found on the user's computer. If there are no +matches, it will use their default typeface. Works the same as the CSS +font-family property. +-} +typeface : [String] -> Text -> Text typeface = Native.Text.typeface {-| Switch to a monospace typeface. Good for code snippets. -} monospace : Text -> Text monospace = Native.Text.monospace -{-| Create a link. -} +{-| Create a link. + + link "http://elm-lang.org" (toText "Elm Website") +-} link : String -> Text -> Text link = Native.Text.link -{-| Set the height of text in pixels. -} height : Float -> Text -> Text height = Native.Text.height -{-| Set the color of a string. -} color : Color -> Text -> Text color = Native.Text.color -{-| Make a string bold. -} bold : Text -> Text bold = Native.Text.bold -{-| Italicize a string. -} italic : Text -> Text italic = Native.Text.italic -{-| Draw a line above a string. -} -overline : Text -> Text -overline = Native.Text.overline +line : Line -> Text -> Text +line = Native.Text.line -{-| Underline a string. -} -underline : Text -> Text -underline = Native.Text.underline +leftAligned : Text -> Element +leftAligned = Native.Text.leftAligned -{-| Draw a line through a string. -} -strikeThrough : Text -> Text -strikeThrough = Native.Text.strikeThrough +rightAligned : Text -> Element +rightAligned = Native.Text.rightAligned -{-| Display justified, styled text. -} -justified : Text -> Element -justified = Native.Text.justified - -{-| Display centered, styled text. -} centered : Text -> Element centered = Native.Text.centered -{-| Display right justified, styled text. -} -righted : Text -> Element -righted = Native.Text.righted - -{-| Display styled text. -} -text : Text -> Element -text = Native.Text.text +justified : Text -> Element +justified = Native.Text.justified {-| Display a plain string. -} plainText : String -> Element -plainText = Native.Text.plainText +plainText str = + leftAligned (toText str) {-| for internal use only -} markdown : Element @@ -107,4 +122,5 @@ the browser: Excellent for debugging. -} asText : a -> Element -asText = Native.Text.asText +asText value = + leftAligned (monospace (toText (show value))) From 7e901a668f53655af918a05aa9ad0b70826bb9a6 Mon Sep 17 00:00:00 2001 From: Evan Czaplicki Date: Sat, 1 Mar 2014 20:08:38 -0800 Subject: [PATCH 02/11] Move conversion from colors to CSS to the Color library --- libraries/Native/Color.js | 11 +++++++++-- libraries/Native/Text.js | 16 ++++------------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/libraries/Native/Color.js b/libraries/Native/Color.js index 8c44c98..80aeec0 100644 --- a/libraries/Native/Color.js +++ b/libraries/Native/Color.js @@ -6,6 +6,12 @@ Elm.Native.Color.make = function(elm) { var Utils = Elm.Native.Utils.make(elm); + function toCss(c) { + return (c._3 === 1) + ? ('rgb(' + c._0 + ', ' + c._1 + ', ' + c._2 + ')') + : ('rgba(' + c._0 + ', ' + c._1 + ', ' + c._2 + ', ' + c._3 + ')'); + } + function complement(rgb) { var hsv = toHSV(rgb); hsv.hue = (hsv.hue + 180) % 360; @@ -63,7 +69,8 @@ Elm.Native.Color.make = function(elm) { return elm.Native.Color.values = { hsva:F4(hsva), hsv:F3(hsv), - complement:complement + complement:complement, + toCss:toCss }; -}; \ No newline at end of file +}; diff --git a/libraries/Native/Text.js b/libraries/Native/Text.js index f777a97..ca0bf22 100644 --- a/libraries/Native/Text.js +++ b/libraries/Native/Text.js @@ -4,6 +4,7 @@ Elm.Native.Text.make = function(elm) { elm.Native.Text = elm.Native.Text || {}; if (elm.Native.Text.values) return elm.Native.Text.values; + var Color = Elm.Native.Color.make(elm); var Element = Elm.Graphics.Element.make(elm); var List = Elm.Native.List.make(elm); var Utils = Elm.Native.Utils.make(elm); @@ -69,16 +70,10 @@ Elm.Native.Text.make = function(elm) { ctor === 'Over' ? 'overline' : 'line-through'; return 'text-decoration:' + decoration + ';'; } - function toColor(c) { - var color = (c._3 === 1) - ? ('rgb(' + c._0 + ', ' + c._1 + ', ' + c._2 + ')') - : ('rgba(' + c._0 + ', ' + c._1 + ', ' + c._2 + ', ' + c._3 + ')'); - return 'color:' + color + ';'; - } // setting styles of Text function style(style, text) { - var newText = ' Date: Sat, 1 Mar 2014 20:09:30 -0800 Subject: [PATCH 03/11] Properly import show from the String library --- libraries/Text.elm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/Text.elm b/libraries/Text.elm index 1799cb7..ef07504 100644 --- a/libraries/Text.elm +++ b/libraries/Text.elm @@ -25,6 +25,7 @@ There are also a bunch of functions to set parts of a `Style` individually: -} import Basics (..) +import String import Color (Color) import Graphics.Element (Element, Three, Pos, ElementPrim, Properties) import Maybe (Maybe) @@ -123,4 +124,4 @@ Excellent for debugging. -} asText : a -> Element asText value = - leftAligned (monospace (toText (show value))) + leftAligned (monospace (toText (String.show value))) From e53ebfca304ad63cb8e30defccdaa58c950416a4 Mon Sep 17 00:00:00 2001 From: Evan Czaplicki Date: Sat, 1 Mar 2014 20:23:01 -0800 Subject: [PATCH 04/11] Add a defaultStyle --- libraries/Text.elm | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/libraries/Text.elm b/libraries/Text.elm index ef07504..e98ec2a 100644 --- a/libraries/Text.elm +++ b/libraries/Text.elm @@ -8,8 +8,8 @@ module Text where # Creating Elements @docs leftAligned, rightAligned, centered, justified -# Style and Links -@docs Style, style, Line, link +# Links and Style +@docs link, Style, style, Line, defaultStyle # Convenience Functions @@ -26,9 +26,9 @@ There are also a bunch of functions to set parts of a `Style` individually: import Basics (..) import String -import Color (Color) +import Color (Color, black) import Graphics.Element (Element, Three, Pos, ElementPrim, Properties) -import Maybe (Maybe) +import Maybe (Maybe, Nothing) import JavaScript (JSString) import Native.Text @@ -47,6 +47,27 @@ type Style = , line : Maybe Line } +{-| Plain black text. It uses the browsers default typeface and text height. +No decorations are used: + + { typeface = [] + , height = Nothing + , color = black + , bold = False + , italic = False + , line = Nothing + } +-} +defaultStyle : Style +defaultStyle = + { typeface = [] + , height = Nothing + , color = black + , bold = False + , italic = False + , line = Nothing + } + {-| Convert a string into text which can be styled and displayed. -} toText : String -> Text toText = Native.Text.toText From 9df2eac7c1be742db530dee8188947e32638003f Mon Sep 17 00:00:00 2001 From: Evan Czaplicki Date: Sat, 1 Mar 2014 20:23:08 -0800 Subject: [PATCH 05/11] Expose toTypefaces and toLine for use within the core libraries --- libraries/Native/Text.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/Native/Text.js b/libraries/Native/Text.js index ca0bf22..c4fb498 100644 --- a/libraries/Native/Text.js +++ b/libraries/Native/Text.js @@ -66,9 +66,8 @@ Elm.Native.Text.make = function(elm) { } function toLine(line) { var ctor = line.ctor; - var decoration = ctor === 'Under' ? 'underline' : - ctor === 'Over' ? 'overline' : 'line-through'; - return 'text-decoration:' + decoration + ';'; + return ctor === 'Under' ? 'underline' : + ctor === 'Over' ? 'overline' : 'line-through'; } // setting styles of Text @@ -87,7 +86,7 @@ Elm.Native.Text.make = function(elm) { newText += 'font-style:italic;'; } if (style.line.ctor !== 'Nothing') { - newText += toLine(style.line._0); + newText += 'text-decoration:' + toLine(style.line._0) + ';'; } newText += '">' + Utils.makeText(text) + '' return Utils.txt(newText); @@ -111,7 +110,7 @@ Elm.Native.Text.make = function(elm) { return { href: toText(href), text:text }; } function line(line, text) { - return { style: toLine(line), text:text }; + return { style: 'text-decoration:' + toLine(line) + ';', text:text }; } function color(color, text) { @@ -161,5 +160,8 @@ Elm.Native.Text.make = function(elm) { centered : block('center'), justified : block('justify'), markdown : markdown, + + toTypefaces:toTypefaces, + toLine:toLine, }; }; From 61aab6292f143ebfde35aed70cbededce0d91d9e Mon Sep 17 00:00:00 2001 From: Evan Czaplicki Date: Sat, 1 Mar 2014 21:04:10 -0800 Subject: [PATCH 06/11] Properly refer to the new Color.toCss function --- libraries/Native/Text.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Native/Text.js b/libraries/Native/Text.js index c4fb498..4d7d4c2 100644 --- a/libraries/Native/Text.js +++ b/libraries/Native/Text.js @@ -4,7 +4,7 @@ Elm.Native.Text.make = function(elm) { elm.Native.Text = elm.Native.Text || {}; if (elm.Native.Text.values) return elm.Native.Text.values; - var Color = Elm.Native.Color.make(elm); + var toCss = Elm.Native.Color.make(elm).toCss; var Element = Elm.Graphics.Element.make(elm); var List = Elm.Native.List.make(elm); var Utils = Elm.Native.Utils.make(elm); From 9423af410f35c09783a5b607d61d5b29ccb143df Mon Sep 17 00:00:00 2001 From: Evan Czaplicki Date: Sat, 1 Mar 2014 21:04:33 -0800 Subject: [PATCH 07/11] Use Text.leftAligned instead of old Text.text in the runtime --- runtime/Init.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/Init.js b/runtime/Init.js index fd96d76..cc92a61 100644 --- a/runtime/Init.js +++ b/runtime/Init.js @@ -91,7 +91,7 @@ function init(display, container, module, ports, moduleToReplace) { checkPorts(elm); } catch(e) { var directions = "
    Open the developer console for more details." - Module.main = Elm.Text.make(elm).text('' + e.message + directions + ''); + Module.main = Elm.Text.make(elm).leftAligned('' + e.message + directions + ''); reportAnyErrors = function() { throw e; } } inputs = ElmRuntime.filterDeadInputs(inputs); From ae513a7f6eb434a4c299e2e4be16a67880a07428 Mon Sep 17 00:00:00 2001 From: Evan Czaplicki Date: Sat, 1 Mar 2014 22:07:17 -0800 Subject: [PATCH 08/11] Remove dead code --- libraries/Native/Text.js | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/Native/Text.js b/libraries/Native/Text.js index 4d7d4c2..e79680e 100644 --- a/libraries/Native/Text.js +++ b/libraries/Native/Text.js @@ -8,7 +8,6 @@ Elm.Native.Text.make = function(elm) { var Element = Elm.Graphics.Element.make(elm); var List = Elm.Native.List.make(elm); var Utils = Elm.Native.Utils.make(elm); - var show = Elm.Native.Show.make(elm).show; function makeSpaces(s) { if (s.length == 0) { return s; } From 10506b5663c5b1e020844d7d6417ed356f0dcee2 Mon Sep 17 00:00:00 2001 From: Evan Czaplicki Date: Sat, 1 Mar 2014 22:07:48 -0800 Subject: [PATCH 09/11] Rely directly on Native.Show.show when needed --- libraries/Native/String.js | 2 -- libraries/String.elm | 3 ++- libraries/Text.elm | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/Native/String.js b/libraries/Native/String.js index c1c4d0e..0bf7bf0 100644 --- a/libraries/Native/String.js +++ b/libraries/Native/String.js @@ -10,7 +10,6 @@ Elm.Native.String.make = function(elm) { var Maybe = Elm.Maybe.make(elm); var JS = Elm.JavaScript.make(elm); var Utils = Elm.Native.Utils.make(elm); - var show = Elm.Native.Show.make(elm); function isEmpty(str) { return str.length === 0; @@ -244,7 +243,6 @@ Elm.Native.String.make = function(elm) { endsWith: F2(endsWith), indexes: F2(indexes), - show:show, toInt: toInt, toFloat: toFloat, toList: toList, diff --git a/libraries/String.elm b/libraries/String.elm index 37faf99..586cc4c 100644 --- a/libraries/String.elm +++ b/libraries/String.elm @@ -28,6 +28,7 @@ Cosmetic operations such as padding with extra characters or trimming whitespace @docs map, filter, foldl, foldr, any, all -} +import Native.Show import Native.String import Maybe (Maybe) @@ -284,7 +285,7 @@ indices = Native.String.indexes show [1,2] == "[1,2]" -} show : a -> String -show = Native.String.show +show = Native.Show.show {-| Try to convert a string into an int, failing on improperly formatted strings. diff --git a/libraries/Text.elm b/libraries/Text.elm index e98ec2a..3e5b6df 100644 --- a/libraries/Text.elm +++ b/libraries/Text.elm @@ -30,6 +30,7 @@ import Color (Color, black) import Graphics.Element (Element, Three, Pos, ElementPrim, Properties) import Maybe (Maybe, Nothing) import JavaScript (JSString) +import Native.Show import Native.Text data Text = Text @@ -145,4 +146,4 @@ Excellent for debugging. -} asText : a -> Element asText value = - leftAligned (monospace (toText (String.show value))) + leftAligned (monospace (toText (Native.Show.show value))) From 093d7afb34cc6be65004c790cdd0848b4d93c452 Mon Sep 17 00:00:00 2001 From: Evan Czaplicki Date: Sat, 1 Mar 2014 22:43:21 -0800 Subject: [PATCH 10/11] Move all field related stuff to Graphics.Input.Field and add the ability to style fields --- libraries/Graphics/Input.elm | 61 +------------- libraries/Graphics/Input/Field.elm | 130 +++++++++++++++++++++++++++++ libraries/Native/Graphics/Input.js | 66 +++++++++++++-- runtime/Render/Element.js | 23 ++++- 4 files changed, 213 insertions(+), 67 deletions(-) create mode 100644 libraries/Graphics/Input/Field.elm diff --git a/libraries/Graphics/Input.elm b/libraries/Graphics/Input.elm index 246f60f..0ae2dcf 100644 --- a/libraries/Graphics/Input.elm +++ b/libraries/Graphics/Input.elm @@ -19,14 +19,15 @@ examples in this library, so just read on to get a better idea of how it works! @docs Input, input # Basic Input Elements -Text fields come later. + +To learn about text fields, see the +[`Graphics.Input.Field`](Graphics-Input-Field) library. + @docs button, customButton, checkbox, dropDown # Clicks and Hovers @docs clickable, hoverable -# Text Fields -@docs field, password, email, noContent, FieldContent, Selection, Direction -} import Signal (Signal) @@ -175,57 +176,3 @@ distinguished with IDs or more complex data structures. -} clickable : Handle a -> a -> Element -> Element clickable = Native.Graphics.Input.clickable - -{-| Represents the current content of a text field. For example: - - FieldContent "She sells sea shells" (Selection 0 3 Backward) - -This means the user highlighted the substring `"She"` backwards. --} -type FieldContent = { string:String, selection:Selection } - -{-| The selection within a text field. `start` is never greater than `end`: - - Selection 0 0 Forward -- cursor precedes all characters - - Selection 5 9 Backward -- highlighting characters starting after - -- the 5th and ending after the 9th --} -type Selection = { start:Int, end:Int, direction:Direction } - -{-| The direction of selection.-} -data Direction = Forward | Backward - -{-| A field with no content: - - FieldContent "" (Selection 0 0 Forward) --} -noContent : FieldContent -noContent = FieldContent "" (Selection 0 0 Forward) - -{-| Create a text field. The following example creates a time-varying element -called `nameField`. As the user types their name, the field will be updated -to match what they have entered. - - name : Input FieldContent - name = input noContent - - nameField : Signal Element - nameField = field name.handle id "Name" <~ name.signal --} -field : Handle a -> (FieldContent -> a) -> String -> FieldContent -> Element -field = Native.Graphics.Input.field - -{-| Same as `field` but the UI element blocks out each characters. -} -password : Handle a -> (FieldContent -> a) -> String -> FieldContent -> Element -password = Native.Graphics.Input.password - -{-| Same as `field` but it adds an annotation that this field is for email -addresses. This is helpful for auto-complete and for mobile users who may -get a custom keyboard with an `@` and `.com` button. --} -email : Handle a -> (FieldContent -> a) -> String -> FieldContent -> Element -email = Native.Graphics.Input.email - --- area : Handle a -> (FieldContent -> a) -> Handle b -> ((Int,Int) -> b) -> (Int,Int) -> String -> FieldContent -> Element --- area = Native.Graphics.Input.area diff --git a/libraries/Graphics/Input/Field.elm b/libraries/Graphics/Input/Field.elm new file mode 100644 index 0000000..c88e27b --- /dev/null +++ b/libraries/Graphics/Input/Field.elm @@ -0,0 +1,130 @@ +module Graphics.Input.Field where +{-| This library specifically addresses text fields. It uses the same genral +approach as the [`Graphics.Input`](Graphics-Input) module for describing an +`Input`, so this library focuses on creating and styling text fields. + +# Create Fields +@docs field, password, email + +# Field Content +@docs Content, Selection, Direction, noContent + +# Field Style +@docs Style, Outline, noOutline, Highlight, noHighlight, Dimensions, uniformly +-} + +import Color (Color) +import Color +import Graphics.Element (Element) +import Graphics.Input (Input, Handle) +import Native.Graphics.Input +import Text + +{-| Easily create uniform dimensions: + + uniformly 4 == { left=4, right=4, top=4, bottom=4 } +-} +uniformly : Int -> Dimensions +uniformly n = Dimensions n n n n + +{-| For setting dimensions of a fields padding or border. The left, right, top, +and bottom may all have different sizes. +-} +type Dimensions = { left:Int, right:Int, top:Int, bottom:Int } + +{-| A field can have a outline around it. This lets you set its color, width, +and radius. The radius allows you to round the corners of your field. Set the +width to zero to make it invisible. +-} +type Outline = { color:Color, width:Dimensions, radius:Int } + +{-| An outline with zero width, so you cannot see it. -} +noOutline : Outline +noOutline = Outline Color.grey (uniformly 0) 0 + +{-| When a field is selected, it has an highlight around it by default. Set the +width of the `Highlight` to zero to make it go away. +-} +type Highlight = { color:Color, width:Int } + +{-| An highlight with zero width, so you cannot see it. -} +noHighlight : Highlight +noHighlight = Highlight Color.blue 0 + +{-| Describes the style of a text box. The `style` field describes the style +of the text itself. The `outline` field describes the glowing blue outline that +shows up when the field has focus. Turn off `outline` by setting its width to +zero. The +-} +type Style = + { padding : Dimensions + , outline : Outline + , highlight : Highlight + , style : Text.Style + } + +{-| The default style for a text field. The outline is `Color.grey` with width +1 and radius 2. The highlight is `Color.blue` with width 1, and the default +text color is black. +-} +defaultStyle : Style +defaultStyle = + { padding = uniformly 4 + , outline = Outline Color.grey (uniformly 1) 2 + , highlight = Highlight Color.blue 1 + , style = Text.defaultStyle + } + +{-| Represents the current content of a text field. For example: + + Content "She sells sea shells" (Selection 0 3 Backward) + +This means the user highlighted the substring `"She"` backwards. +-} +type Content = { string:String, selection:Selection } + +{-| The selection within a text field. `start` is never greater than `end`: + + Selection 0 0 Forward -- cursor precedes all characters + + Selection 5 9 Backward -- highlighting characters starting after + -- the 5th and ending after the 9th +-} +type Selection = { start:Int, end:Int, direction:Direction } + +{-| The direction of selection.-} +data Direction = Forward | Backward + +{-| A field with no content: + + Content "" (Selection 0 0 Forward) +-} +noContent : Content +noContent = Content "" (Selection 0 0 Forward) + +{-| Create a text field. The following example creates a time-varying element +called `nameField`. As the user types their name, the field will be updated +to match what they have entered. + + name : Input Content + name = input noContent + + nameField : Signal Element + nameField = field name.handle id "Name" <~ name.signal +-} +field : Handle a -> (Content -> a) -> Style -> String -> Content -> Element +field = Native.Graphics.Input.field + +{-| Same as `field` but the UI element blocks out each characters. -} +password : Handle a -> (Content -> a) -> Style -> String -> Content -> Element +password = Native.Graphics.Input.password + +{-| Same as `field` but it adds an annotation that this field is for email +addresses. This is helpful for auto-complete and for mobile users who may +get a custom keyboard with an `@` and `.com` button. +-} +email : Handle a -> (Content -> a) -> Style -> String -> Content -> Element +email = Native.Graphics.Input.email + +-- area : Handle a -> (Content -> a) -> Handle b -> ((Int,Int) -> b) -> (Int,Int) -> String -> Content -> Element +-- area = Native.Graphics.Input.area diff --git a/libraries/Native/Graphics/Input.js b/libraries/Native/Graphics/Input.js index 60dde71..2b2c01a 100644 --- a/libraries/Native/Graphics/Input.js +++ b/libraries/Native/Graphics/Input.js @@ -8,6 +8,8 @@ Elm.Native.Graphics.Input.make = function(elm) { var Render = ElmRuntime.use(ElmRuntime.Render.Element); var newNode = ElmRuntime.use(ElmRuntime.Render.Utils).newElement; + var toCss = Elm.Native.Color.make(elm).toCss; + var Text = Elm.Native.Text.make(elm); var Signal = Elm.Signal.make(elm); var newElement = Elm.Graphics.Element.make(elm).newElement; var JS = Elm.Native.JavaScript.make(elm); @@ -215,11 +217,52 @@ Elm.Native.Graphics.Input.make = function(elm) { } } + function updateIfNeeded(css, attribute, latestAttribute) { + if (css[attribute] !== latestAttribute) { + css[attribute] = latestAttribute; + } + } + function cssDimensions(dimensions) { + return dimensions.top + 'px ' + + dimensions.right + 'px ' + + dimensions.bottom + 'px ' + + dimensions.left + 'px'; + } + function updateFieldStyle(css, style) { + updateIfNeeded(css, 'padding', cssDimensions(style.padding)); + + var outline = style.outline; + updateIfNeeded(css, 'border-width', cssDimensions(outline.width)); + updateIfNeeded(css, 'border-color', toCss(outline.color)); + updateIfNeeded(css, 'border-radius', outline.radius + 'px'); + + var highlight = style.highlight; + if (highlight.width === 0) { + css.outline = 'none'; + } else { + updateIfNeeded(css, 'outline-width', highlight.width + 'px'); + updateIfNeeded(css, 'outline-color', toCss(highlight.color)); + } + + var textStyle = style.style; + updateIfNeeded(css, 'color', toCss(textStyle.color)); + if (textStyle.typeface.ctor !== '[]') { + updateIfNeeded(css, 'font-family', Text.toTypefaces(textStyle.typeface)); + } + if (textStyle.height.ctor !== "Nothing") { + updateIfNeeded(css, 'font-size', textStyle.height._0 + 'px'); + } + updateIfNeeded(css, 'font-weight', textStyle.bold ? 'bold' : 'normal'); + updateIfNeeded(css, 'font-style', textStyle.italic ? 'italic' : 'normal'); + if (textStyle.line.ctor !== 'Nothing') { + updateIfNeeded(css, 'text-decoration', Text.toLine(textStyle.line._0)); + } + } + function renderField(model) { var field = newNode('input'); - field.style.border = 'none'; - field.style.outline = 'none'; - field.style.backgroundColor = 'transparent'; + updateFieldStyle(field.style, model.style); + field.style.borderStyle = 'solid'; field.style.pointerEvents = 'auto'; field.type = model.type; @@ -302,6 +345,9 @@ Elm.Native.Graphics.Input.make = function(elm) { } function updateField(field, oldModel, newModel) { + if (oldModel.style !== newModel.style) { + updateFieldStyle(field.style, newModel.style); + } field.elm_signal = newModel.signal; field.elm_handler = newModel.handler; @@ -316,10 +362,16 @@ Elm.Native.Graphics.Input.make = function(elm) { } function mkField(type) { - function field(signal, handler, placeHolder, content) { + function field(signal, handler, style, placeHolder, content) { + var padding = style.padding; + var outline = style.outline.width; + var adjustWidth = padding.left + padding.right + outline.left + outline.right; + var adjustHeight = padding.top + padding.bottom + outline.top + outline.bottom; return A3(newElement, 200, 30, { ctor: 'Custom', - type: type + 'Input', + type: type + 'Field', + adjustWidth: adjustWidth, + adjustHeight: adjustHeight, render: renderField, update: updateField, model: { @@ -327,14 +379,14 @@ Elm.Native.Graphics.Input.make = function(elm) { handler:handler, placeHolder:placeHolder, content:content, + style:style, type:type } }); } - return F4(field); + return F5(field); } - function hoverable(signal, handler, elem) { function onHover(bool) { elm.notify(signal.id, handler(bool)); diff --git a/runtime/Render/Element.js b/runtime/Render/Element.js index c472752..a93afbb 100644 --- a/runtime/Render/Element.js +++ b/runtime/Render/Element.js @@ -7,7 +7,15 @@ var newElement = Utils.newElement, extract = Utils.extract, addTransform = Utils.addTransform, removeTransform = Utils.removeTransform, fromList = Utils.fromList, eq = Utils.eq; -function setProps(props, e) { +function setProps(elem, e) { + var props = elem.props; + var element = elem.element; + if (element.adjustWidth) { + props.width -= element.adjustWidth; + } + if (element.adjustHeight) { + props.height -= element.adjustHeight; + } e.style.width = (props.width |0) + 'px'; e.style.height = (props.height|0) + 'px'; if (props.opacity !== 1) { e.style.opacity = props.opacity; } @@ -200,7 +208,7 @@ function rawHtml(elem) { return div; } -function render(elem) { return setProps(elem.props, makeElement(elem)); } +function render(elem) { return setProps(elem, makeElement(elem)); } function makeElement(e) { var elem = e.element; switch(elem.ctor) { @@ -308,7 +316,16 @@ function update(node, curr, next) { } function updateProps(node, curr, next) { - var props = next.props, currP = curr.props, e = node; + var props = next.props; + var currP = curr.props; + var e = node; + var element = next.element; + if (element.adjustWidth) { + props.width -= element.adjustWidth; + } + if (element.adjustHeight) { + props.height -= element.adjustHeight; + } if (props.width !== currP.width) e.style.width = (props.width |0) + 'px'; if (props.height !== currP.height) e.style.height = (props.height|0) + 'px'; if (props.opacity !== 1 && props.opacity !== currP.opacity) { From 4c43439cdbf102826cc70be70157f4fd286075e7 Mon Sep 17 00:00:00 2001 From: Evan Czaplicki Date: Sun, 2 Mar 2014 12:43:32 -1000 Subject: [PATCH 11/11] Fix typo that caused runtime error with buttons --- libraries/Native/Graphics/Input.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Native/Graphics/Input.js b/libraries/Native/Graphics/Input.js index 2b2c01a..0094398 100644 --- a/libraries/Native/Graphics/Input.js +++ b/libraries/Native/Graphics/Input.js @@ -80,7 +80,7 @@ Elm.Native.Graphics.Input.make = function(elm) { function updateButton(node, oldModel, newModel) { node.elm_signal = newModel.signal; - node.elm_value = nemModel.value; + node.elm_value = newModel.value; var txt = newModel.text; if (oldModel.text !== txt) node.innerHTML = txt; }