Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement single-line text input #3585

Merged
merged 10 commits into from Nov 13, 2014

Add single-line text input with no visible cursor.

  • Loading branch information
jdm committed Nov 13, 2014
commit 80764f65e3e4895d3a012d6bc1d3bb8183ae15da
@@ -58,6 +58,7 @@ pub enum KeyState {
}

//N.B. Straight up copied from glfw-rs
#[deriving(Show)]
pub enum Key {
KeySpace,
KeyApostrophe,
@@ -184,10 +185,10 @@ pub enum Key {

bitflags! {
flags KeyModifiers: u8 {
const Shift = 0x01,
const Control = 0x02,
const Alt = 0x04,
const Super = 0x08,
const SHIFT = 0x01,
const CONTROL = 0x02,
const ALT = 0x04,
const SUPER = 0x08,
}
}

@@ -26,7 +26,7 @@ use dom::document::{Document, DocumentHelpers, LayoutDocumentHelpers};
use dom::domtokenlist::DOMTokenList;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlcollection::HTMLCollection;
use dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
use dom::htmlinputelement::{HTMLInputElement, RawLayoutHTMLInputElementHelpers};
use dom::htmlserializer::serialize;
use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelpers};
use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_from_node};
@@ -231,17 +231,24 @@ pub trait RawLayoutElementHelpers {
-> Option<i32>;
}

#[inline]
#[allow(unrooted_must_root)]
unsafe fn get_attr_for_layout<'a>(elem: &'a Element, namespace: &Namespace, name: &Atom) -> Option<&'a JS<Attr>> {
// cast to point to T in RefCell<T> directly
let attrs: *const Vec<JS<Attr>> = mem::transmute(&elem.attrs);
(*attrs).iter().find(|attr: & &JS<Attr>| {
let attr = attr.unsafe_get();
*name == (*attr).local_name_atom_forever() &&
(*attr).namespace() == namespace
})
}

impl RawLayoutElementHelpers for Element {
#[inline]
#[allow(unrooted_must_root)]
unsafe fn get_attr_val_for_layout<'a>(&'a self, namespace: &Namespace, name: &Atom)
-> Option<&'a str> {
let attrs = self.attrs.borrow_for_layout();
(*attrs).iter().find(|attr: & &JS<Attr>| {
let attr = attr.unsafe_get();
*name == (*attr).local_name_atom_forever() &&
(*attr).namespace() == namespace
}).map(|attr| {
get_attr_for_layout(self, namespace, name).map(|attr| {
let attr = attr.unsafe_get();
(*attr).value_ref_forever()
})
@@ -337,6 +344,7 @@ impl RawLayoutElementHelpers for Element {

pub trait LayoutElementHelpers {
unsafe fn html_element_in_html_document_for_layout(&self) -> bool;
unsafe fn has_attr_for_layout(&self, namespace: &Namespace, name: &Atom) -> bool;
}

impl LayoutElementHelpers for JS<Element> {
@@ -349,6 +357,10 @@ impl LayoutElementHelpers for JS<Element> {
let node: JS<Node> = self.transmute_copy();
node.owner_doc_for_layout().is_html_document_for_layout()
}

unsafe fn has_attr_for_layout(&self, namespace: &Namespace, name: &Atom) -> bool {
get_attr_for_layout(&*self.unsafe_get(), namespace, name).is_some()
}
}

pub trait ElementHelpers<'a> {
@@ -13,16 +13,20 @@ use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementM
use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLFormElementCast, HTMLInputElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLFieldSetElementDerived};
use dom::bindings::codegen::InheritTypes::KeyboardEventCast;
use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, ResultRootable};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::{Document, DocumentHelpers};
use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId};
use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId, LayoutElementHelpers};
use dom::element::RawLayoutElementHelpers;
use dom::event::Event;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::keyboardevent::KeyboardEvent;
use dom::htmlformelement::{InputElement, FormOwner, HTMLFormElement, HTMLFormElementHelpers, NotFromFormSubmitMethod};
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, document_from_node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use textinput::{TextInput, TriggerDefaultAction, DispatchInput, Nothing};

use servo_util::str::DOMString;
use string_cache::Atom;
@@ -51,9 +55,8 @@ pub struct HTMLInputElement {
htmlelement: HTMLElement,
input_type: Cell<InputType>,
checked: Cell<bool>,
uncommitted_value: DOMRefCell<Option<String>>,
value: DOMRefCell<Option<String>>,
size: Cell<u32>,
textinput: DOMRefCell<TextInput>,
}

impl HTMLInputElementDerived for EventTarget {
@@ -70,9 +73,8 @@ impl HTMLInputElement {
htmlelement: HTMLElement::new_inherited(HTMLInputElementTypeId, localName, prefix, document),
input_type: Cell::new(InputText),
checked: Cell::new(false),
uncommitted_value: DOMRefCell::new(None),
value: DOMRefCell::new(None),
size: Cell::new(DEFAULT_INPUT_SIZE),
textinput: DOMRefCell::new(TextInput::new(false, "".to_string())),
}
}

@@ -84,40 +86,55 @@ impl HTMLInputElement {
}

pub trait LayoutHTMLInputElementHelpers {
unsafe fn get_value_for_layout(&self) -> String;
unsafe fn get_value_for_layout(self) -> String;
unsafe fn get_size_for_layout(self) -> u32;
}

pub trait RawLayoutHTMLInputElementHelpers {
unsafe fn get_size_for_layout(&self) -> u32;
}

impl LayoutHTMLInputElementHelpers for HTMLInputElement {
impl LayoutHTMLInputElementHelpers for JS<HTMLInputElement> {
#[allow(unrooted_must_root)]
unsafe fn get_value_for_layout(&self) -> String {
match self.input_type.get() {
unsafe fn get_value_for_layout(self) -> String {
unsafe fn get_raw_textinput_value(input: JS<HTMLInputElement>) -> Option<String> {
let elem: JS<Element> = input.transmute_copy();
if !elem.has_attr_for_layout(&ns!(""), &atom!("value")) {
return None;
}
Some((*input.unsafe_get()).textinput.borrow_for_layout().get_content())
}

unsafe fn get_raw_attr_value(input: JS<HTMLInputElement>) -> Option<String> {
let elem: JS<Element> = input.transmute_copy();
(*elem.unsafe_get()).get_attr_val_for_layout(&ns!(""), &atom!("value"))
.map(|s| s.to_string())
}

match (*self.unsafe_get()).input_type.get() {
InputCheckbox | InputRadio => "".to_string(),
InputFile | InputImage => "".to_string(),
InputButton(ref default) => self.value.borrow_for_layout().clone()
InputButton(ref default) => get_raw_attr_value(self)
.or_else(|| default.map(|v| v.to_string()))
.unwrap_or_else(|| "".to_string()),
InputPassword => {
let raw = self.value.borrow_for_layout().clone().unwrap_or_else(|| "".to_string());
String::from_char(raw.len(), '*')
let raw = get_raw_textinput_value(self).unwrap_or_else(|| "".to_string());
String::from_char(raw.len(), '')
}
_ => self.value.borrow_for_layout().clone().unwrap_or_else(|| "".to_string()),
_ => get_raw_textinput_value(self).unwrap_or_else(|| "".to_string()),
}
}

#[allow(unrooted_must_root)]
unsafe fn get_size_for_layout(&self) -> u32 {
self.size.get()
unsafe fn get_size_for_layout(self) -> u32 {
(*self.unsafe_get()).get_size_for_layout()
}
}

impl LayoutHTMLInputElementHelpers for JS<HTMLInputElement> {
unsafe fn get_value_for_layout(&self) -> String {
(*self.unsafe_get()).get_value_for_layout()
}

impl RawLayoutHTMLInputElementHelpers for HTMLInputElement {
#[allow(unrooted_must_root)]
unsafe fn get_size_for_layout(&self) -> u32 {
(*self.unsafe_get()).get_size_for_layout()
self.size.get()
}
}

@@ -156,7 +173,7 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> {

// https://html.spec.whatwg.org/multipage/forms.html#dom-input-value
fn Value(self) -> DOMString {
self.value.borrow().clone().unwrap_or("".to_string())
self.textinput.borrow().get_content()
}

// https://html.spec.whatwg.org/multipage/forms.html#dom-input-value
@@ -309,7 +326,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
self.force_relayout();
}
&atom!("value") => {
*self.value.borrow_mut() = Some(attr.value().as_slice().to_string());
self.textinput.borrow_mut().set_content(attr.value().as_slice().to_string());
self.force_relayout();
}
&atom!("name") => {
@@ -353,7 +370,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
self.force_relayout();
}
&atom!("value") => {
*self.value.borrow_mut() = None;
self.textinput.borrow_mut().set_content("".to_string());
self.force_relayout();
}
&atom!("name") => {
@@ -418,6 +435,18 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {

let doc = document_from_node(*self).root();
doc.request_focus(ElementCast::from_ref(*self));
} else if "keydown" == event.Type().as_slice() && !event.DefaultPrevented() &&
(self.input_type.get() == InputText|| self.input_type.get() == InputPassword) {
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 => (),
}
});
}
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.