Auto merge of #10176 - mbrubeck:selection-range, r=pcwalton

Highlight selected text in input fields

Fixes #9993.  This does not yet allow stylesheets to set the selection colors; instead it uses a hard-coded orange background and white foreground.

r? @pcwalton

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/10176)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-03-26 08:16:20 +05:30
commit bed91b3334
17 changed files with 264 additions and 77 deletions

View file

@ -82,6 +82,7 @@ num = "0.1.24"
rand = "0.3"
phf = "0.7.13"
phf_macros = "0.7.13"
range = { path = "../range" }
ref_filter_map = "1.0"
ref_slice = "0.1.0"
regex = "0.1.43"

View file

@ -31,6 +31,7 @@ use dom::nodelist::NodeList;
use dom::validation::Validatable;
use dom::virtualmethods::VirtualMethods;
use msg::constellation_msg::ConstellationChan;
use range::Range;
use script_thread::ScriptThreadEventCategory::InputEvent;
use script_thread::{CommonScriptMsg, Runnable};
use script_traits::ScriptMsg as ConstellationMsg;
@ -209,7 +210,7 @@ pub trait LayoutHTMLInputElementHelpers {
#[allow(unsafe_code)]
unsafe fn get_size_for_layout(self) -> u32;
#[allow(unsafe_code)]
unsafe fn get_insertion_point_index_for_layout(self) -> Option<isize>;
unsafe fn get_selection_for_layout(self) -> Option<Range<isize>>;
#[allow(unsafe_code)]
unsafe fn get_checked_state_for_layout(self) -> bool;
#[allow(unsafe_code)]
@ -242,7 +243,7 @@ impl LayoutHTMLInputElementHelpers for LayoutJS<HTMLInputElement> {
InputType::InputPassword => {
let text = get_raw_textinput_value(self);
if !text.is_empty() {
// The implementation of get_insertion_point_index_for_layout expects a 1:1 mapping of chars.
// The implementation of get_selection_for_layout expects a 1:1 mapping of chars.
text.chars().map(|_| '●').collect()
} else {
String::from((*self.unsafe_get()).placeholder.borrow_for_layout().clone())
@ -251,7 +252,7 @@ impl LayoutHTMLInputElementHelpers for LayoutJS<HTMLInputElement> {
_ => {
let text = get_raw_textinput_value(self);
if !text.is_empty() {
// The implementation of get_insertion_point_index_for_layout expects a 1:1 mapping of chars.
// The implementation of get_selection_for_layout expects a 1:1 mapping of chars.
String::from(text)
} else {
String::from((*self.unsafe_get()).placeholder.borrow_for_layout().clone())
@ -268,25 +269,24 @@ impl LayoutHTMLInputElementHelpers for LayoutJS<HTMLInputElement> {
#[allow(unrooted_must_root)]
#[allow(unsafe_code)]
unsafe fn get_insertion_point_index_for_layout(self) -> Option<isize> {
unsafe fn get_selection_for_layout(self) -> Option<Range<isize>> {
if !(*self.unsafe_get()).upcast::<Element>().get_focus_state() {
return None;
}
match (*self.unsafe_get()).input_type.get() {
InputType::InputText => {
let raw = self.get_value_for_layout();
Some(search_index((*self.unsafe_get()).textinput.borrow_for_layout().edit_point.index,
raw.char_indices()))
}
InputType::InputPassword => {
// Use the raw textinput to get the index as long as we use a 1:1 char mapping
// in get_input_value_for_layout.
let raw = get_raw_textinput_value(self);
Some(search_index((*self.unsafe_get()).textinput.borrow_for_layout().edit_point.index,
raw.char_indices()))
}
_ => None
}
// Use the raw textinput to get the index as long as we use a 1:1 char mapping
// in get_value_for_layout.
let raw = match (*self.unsafe_get()).input_type.get() {
InputType::InputText |
InputType::InputPassword => get_raw_textinput_value(self),
_ => return None
};
let textinput = (*self.unsafe_get()).textinput.borrow_for_layout();
let selection = textinput.get_absolute_selection_range();
let begin_byte = selection.begin();
let begin = search_index(begin_byte, raw.char_indices());
let length = search_index(selection.length(), raw[begin_byte..].char_indices());
Some(Range::new(begin, length))
}
#[allow(unrooted_must_root)]

View file

@ -26,6 +26,7 @@ use dom::nodelist::NodeList;
use dom::validation::Validatable;
use dom::virtualmethods::VirtualMethods;
use msg::constellation_msg::ConstellationChan;
use range::Range;
use script_traits::ScriptMsg as ConstellationMsg;
use std::cell::Cell;
use string_cache::Atom;
@ -46,7 +47,7 @@ pub trait LayoutHTMLTextAreaElementHelpers {
#[allow(unsafe_code)]
unsafe fn get_value_for_layout(self) -> String;
#[allow(unsafe_code)]
unsafe fn get_absolute_insertion_point_for_layout(self) -> Option<usize>;
unsafe fn get_absolute_selection_for_layout(self) -> Option<Range<usize>>;
#[allow(unsafe_code)]
fn get_cols(self) -> u32;
#[allow(unsafe_code)]
@ -62,10 +63,10 @@ impl LayoutHTMLTextAreaElementHelpers for LayoutJS<HTMLTextAreaElement> {
#[allow(unrooted_must_root)]
#[allow(unsafe_code)]
unsafe fn get_absolute_insertion_point_for_layout(self) -> Option<usize> {
unsafe fn get_absolute_selection_for_layout(self) -> Option<Range<usize>> {
if (*self.unsafe_get()).upcast::<Element>().get_focus_state() {
Some((*self.unsafe_get()).textinput.borrow_for_layout()
.get_absolute_insertion_point())
.get_absolute_selection_range())
} else {
None
}

View file

@ -58,6 +58,7 @@ extern crate phf;
#[macro_use]
extern crate profile_traits;
extern crate rand;
extern crate range;
extern crate ref_filter_map;
extern crate ref_slice;
extern crate regex;

View file

@ -8,6 +8,7 @@ use clipboard_provider::ClipboardProvider;
use dom::keyboardevent::{KeyboardEvent, key_value};
use msg::constellation_msg::{ALT, CONTROL, SHIFT, SUPER};
use msg::constellation_msg::{Key, KeyModifiers};
use range::Range;
use std::borrow::ToOwned;
use std::cmp::{max, min};
use std::default::Default;
@ -154,6 +155,15 @@ impl<T: ClipboardProvider> TextInput<T> {
})
}
pub fn get_absolute_selection_range(&self) -> Range<usize> {
match self.get_sorted_selection() {
Some((begin, _end)) =>
Range::new(self.get_absolute_point_for_text_point(&begin), self.selection_len()),
None =>
Range::new(self.get_absolute_insertion_point(), 0)
}
}
pub fn get_selection_text(&self) -> Option<String> {
self.get_sorted_selection().map(|(begin, end)| {
if begin.line != end.line {