Implements multi line text input for TextArea

This commit is contained in:
Matthew Rasmus 2014-11-29 13:48:55 -08:00
parent fd65b5f438
commit 29241699fd
6 changed files with 115 additions and 11 deletions

View file

@ -49,6 +49,7 @@ use script::dom::element::{HTMLObjectElementTypeId, HTMLInputElementTypeId};
use script::dom::element::{HTMLTableColElementTypeId, HTMLTableDataCellElementTypeId};
use script::dom::element::{HTMLTableElementTypeId, HTMLTableHeaderCellElementTypeId};
use script::dom::element::{HTMLTableRowElementTypeId, HTMLTableSectionElementTypeId};
use script::dom::element::HTMLTextAreaElementTypeId;
use script::dom::node::{CommentNodeTypeId, DoctypeNodeTypeId, DocumentFragmentNodeTypeId};
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstructionNodeTypeId};
use script::dom::node::{TextNodeTypeId};
@ -273,7 +274,8 @@ impl<'a> FlowConstructor<'a> {
TableColumnFragment(TableColumnFragmentInfo::new(node))
}
Some(ElementNodeTypeId(HTMLTableDataCellElementTypeId)) |
Some(ElementNodeTypeId(HTMLTableHeaderCellElementTypeId)) => TableCellFragment,
Some(ElementNodeTypeId(HTMLTableHeaderCellElementTypeId)) |
Some(ElementNodeTypeId(HTMLTextAreaElementTypeId)) => TableCellFragment,
Some(ElementNodeTypeId(HTMLTableRowElementTypeId)) |
Some(ElementNodeTypeId(HTMLTableSectionElementTypeId)) => TableRowFragment,
Some(TextNodeTypeId) => UnscannedTextFragment(UnscannedTextFragmentInfo::new(node)),
@ -487,7 +489,16 @@ impl<'a> FlowConstructor<'a> {
// Special case: If this is generated content, then we need to initialize the accumulator
// with the fragment corresponding to that content.
if node.get_pseudo_element_type() != Normal ||
node.type_id() == Some(ElementNodeTypeId(HTMLInputElementTypeId)) {
node.type_id() == Some(ElementNodeTypeId(HTMLInputElementTypeId)) ||
node.type_id() == Some(ElementNodeTypeId(HTMLTextAreaElementTypeId)) {
// A TextArea's text contents are displayed through the input text
// box, so don't construct them.
// TODO Maybe this belongs somewhere else?
if node.type_id() == Some(ElementNodeTypeId(HTMLTextAreaElementTypeId)) {
for kid in node.children() {
kid.set_flow_construction_result(NoConstructionResult)
}
}
let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::new(node));
let fragment = Fragment::new_from_specific_info(node, fragment_info);
inline_fragment_accumulator.fragments.push_back(fragment);

View file

@ -39,13 +39,14 @@ use util::{PrivateLayoutData};
use gfx::display_list::OpaqueNode;
use script::dom::bindings::codegen::InheritTypes::{ElementCast, HTMLIFrameElementCast};
use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementCast, HTMLInputElementCast};
use script::dom::bindings::codegen::InheritTypes::{NodeCast, TextCast};
use script::dom::bindings::codegen::InheritTypes::{HTMLTextAreaElementCast, NodeCast, TextCast};
use script::dom::bindings::js::JS;
use script::dom::element::{Element, HTMLAreaElementTypeId, HTMLAnchorElementTypeId};
use script::dom::element::{HTMLLinkElementTypeId, LayoutElementHelpers, RawLayoutElementHelpers};
use script::dom::htmliframeelement::HTMLIFrameElement;
use script::dom::htmlimageelement::LayoutHTMLImageElementHelpers;
use script::dom::htmlinputelement::LayoutHTMLInputElementHelpers;
use script::dom::htmltextareaelement::LayoutHTMLTextAreaElementHelpers;
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, Node, NodeTypeId};
use script::dom::node::{LayoutNodeHelpers, RawLayoutNodeHelpers, SharedLayoutData};
use script::dom::node::{HAS_CHANGED, IS_DIRTY, HAS_DIRTY_SIBLINGS, HAS_DIRTY_DESCENDANTS};
@ -188,7 +189,10 @@ impl<'ln> TLayoutNode for LayoutNode<'ln> {
Some(text) => (*text.unsafe_get()).characterdata().data_for_layout().to_string(),
None => match HTMLInputElementCast::to_js(self.get_jsmanaged()) {
Some(input) => input.get_value_for_layout(),
None => panic!("not text!")
None => match HTMLTextAreaElementCast::to_js(self.get_jsmanaged()) {
Some(area) => area.get_value_for_layout(),
None => panic!("not text!")
}
}
}
}

View file

@ -4,18 +4,24 @@
use dom::attr::Attr;
use dom::attr::AttrHelpers;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, NodeCast};
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLTextAreaElementDerived, HTMLFieldSetElementDerived};
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::codegen::InheritTypes::{KeyboardEventCast, TextDerived};
use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::document::{Document, DocumentHelpers};
use dom::element::{AttributeHandlers, HTMLTextAreaElementTypeId};
use dom::event::Event;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId};
use dom::keyboardevent::KeyboardEvent;
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, document_from_node};
use textinput::{Multiple, TextInput, TriggerDefaultAction, DispatchInput, Nothing};
use dom::virtualmethods::VirtualMethods;
use servo_util::str::DOMString;
@ -24,6 +30,7 @@ use string_cache::Atom;
#[dom_struct]
pub struct HTMLTextAreaElement {
htmlelement: HTMLElement,
textinput: DOMRefCell<TextInput>,
}
impl HTMLTextAreaElementDerived for EventTarget {
@ -32,10 +39,22 @@ impl HTMLTextAreaElementDerived for EventTarget {
}
}
pub trait LayoutHTMLTextAreaElementHelpers {
unsafe fn get_value_for_layout(self) -> String;
}
impl LayoutHTMLTextAreaElementHelpers for JS<HTMLTextAreaElement> {
#[allow(unrooted_must_root)]
unsafe fn get_value_for_layout(self) -> String {
(*self.unsafe_get()).textinput.borrow_for_layout().get_content()
}
}
impl HTMLTextAreaElement {
fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTextAreaElement {
HTMLTextAreaElement {
htmlelement: HTMLElement::new_inherited(HTMLTextAreaElementTypeId, localName, prefix, document)
htmlelement: HTMLElement::new_inherited(HTMLTextAreaElementTypeId, localName, prefix, document),
textinput: DOMRefCell::new(TextInput::new(Multiple, "".to_string())),
}
}
@ -108,6 +127,28 @@ impl<'a> HTMLTextAreaElementMethods for JSRef<'a, HTMLTextAreaElement> {
let node: JSRef<Node> = NodeCast::from_ref(self);
node.SetTextContent(Some(value))
}
// https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-value
fn Value(self) -> DOMString {
self.textinput.borrow().get_content()
}
// https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-value
fn SetValue(self, value: DOMString) {
self.textinput.borrow_mut().set_content(value);
}
}
pub trait HTMLTextAreaElementHelpers {
fn force_relayout(self);
}
impl<'a> HTMLTextAreaElementHelpers for JSRef<'a, HTMLTextAreaElement> {
fn force_relayout(self) {
let doc = document_from_node(self).root();
let node: JSRef<Node> = NodeCast::from_ref(self);
doc.content_changed(node)
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLTextAreaElement> {
@ -172,6 +213,47 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLTextAreaElement> {
node.check_disabled_attribute();
}
}
fn child_inserted(&self, child: JSRef<Node>) {
match self.super_type() {
Some(s) => {
s.child_inserted(child);
}
_ => (),
}
if child.is_text() {
self.SetValue(child.GetTextContent().unwrap());
}
}
// copied and modified from htmlinputelement.rs
fn handle_event(&self, event: JSRef<Event>) {
match self.super_type() {
Some(s) => {
s.handle_event(event);
}
_ => (),
}
if "click" == event.Type().as_slice() && !event.DefaultPrevented() {
//TODO: set the editing position for text inputs
let doc = document_from_node(*self).root();
doc.request_focus(ElementCast::from_ref(*self));
} else if "keydown" == event.Type().as_slice() && !event.DefaultPrevented() {
let keyevent: Option<JSRef<KeyboardEvent>> = KeyboardEventCast::to_ref(event);
keyevent.map(|event| {
match self.textinput.borrow_mut().handle_keydown(event) {
TriggerDefaultAction => (),
DispatchInput => {
self.force_relayout();
}
Nothing => (),
}
});
}
}
}
impl Reflectable for HTMLTextAreaElement {

View file

@ -23,7 +23,7 @@ interface HTMLTextAreaElement : HTMLElement {
readonly attribute DOMString type;
attribute DOMString defaultValue;
//[TreatNullAs=EmptyString] attribute DOMString value;
[TreatNullAs=EmptyString] attribute DOMString value;
//readonly attribute unsigned long textLength;
//readonly attribute boolean willValidate;

View file

@ -1,5 +1,6 @@
input, select { display: inline-block; }
input, textarea, select { display: inline-block; }
input { background: white; min-height: 1.0em; padding: 0em; padding-left: 0.25em; padding-right: 0.25em; border: solid lightgrey 1px; color: black; white-space: nowrap; }
textarea { background: white; min-height: 1.0em; padding: 0em; padding-left: 0.25em; padding-right: 0.25em; border: solid lightgrey 1px; color: black; white-space: pre; }
input[type="button"],
input[type="submit"],
input[type="reset"] { background: lightgrey; border-top: solid 1px #EEEEEE; border-left: solid 1px #CCCCCC; border-right: solid 1px #999999; border-bottom: solid 1px #999999; text-align: center; vertical-align: middle; color: black; width: 100%; }

6
tests/html/textarea.html Normal file
View file

@ -0,0 +1,6 @@
<html>
<head></head>
<body>
<textarea name="textarea">Write something here
and maybe here</textarea>
</body>