Auto merge of #14291 - fiji-flo:text-input-select, r=pcwalton

Position insertion point in input field with mouse

<!-- Please describe your changes on the following line: -->
Implements cursor positioning in input elements (text/password) via mouse. The related issue is #10083 but is only covered partly.
This PR does **not** cover:
* positioning in textarea elements via mouse
* text selection in input/textarea elements via mouse

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [ ] These changes fix #10083 (github issue number if applicable).

<!-- Either: -->
- [ ] There are tests for these changes OR
- [ ] These changes do not require tests because I can't think of a way to test mouse interaction in the current test pipeline.

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/14291)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-01-11 14:19:10 -08:00 committed by GitHub
commit 68a8e1bf2b
9 changed files with 219 additions and 50 deletions

View file

@ -11,6 +11,8 @@ use dom::bindings::codegen::Bindings::FileListBinding::FileListMethods;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods;
use dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::error::{Error, ErrorResult};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, LayoutJS, MutNullableJS, Root, RootedReference};
@ -27,6 +29,7 @@ use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlformelement::{FormControl, FormDatum, FormDatumValue, FormSubmitter, HTMLFormElement};
use dom::htmlformelement::{ResetFrom, SubmittedFrom};
use dom::keyboardevent::KeyboardEvent;
use dom::mouseevent::MouseEvent;
use dom::node::{Node, NodeDamage, UnbindContext};
use dom::node::{document_from_node, window_from_node};
use dom::nodelist::NodeList;
@ -39,6 +42,7 @@ use mime_guess;
use net_traits::{CoreResourceMsg, IpcSend};
use net_traits::blob_url_store::get_blob_origin;
use net_traits::filemanager_thread::{FileManagerThreadMsg, FilterPattern};
use script_layout_interface::rpc::TextIndexResponse;
use script_traits::ScriptMsg as ConstellationMsg;
use servo_atoms::Atom;
use std::borrow::ToOwned;
@ -1088,6 +1092,33 @@ impl VirtualMethods for HTMLInputElement {
//TODO: set the editing position for text inputs
document_from_node(self).request_focus(self.upcast());
if (self.input_type.get() == InputType::InputText ||
self.input_type.get() == InputType::InputPassword) &&
// Check if we display a placeholder. Layout doesn't know about this.
!self.textinput.borrow().is_empty() {
if let Some(mouse_event) = event.downcast::<MouseEvent>() {
// dispatch_key_event (document.rs) triggers a click event when releasing
// the space key. There's no nice way to catch this so let's use this for
// now.
if !(mouse_event.ScreenX() == 0 && mouse_event.ScreenY() == 0 &&
mouse_event.GetRelatedTarget().is_none()) {
let window = window_from_node(self);
let translated_x = mouse_event.ClientX() + window.PageXOffset();
let translated_y = mouse_event.ClientY() + window.PageYOffset();
let TextIndexResponse(index) = window.text_index_query(
self.upcast::<Node>().to_trusted_node_address(),
translated_x,
translated_y
);
if let Some(i) = index {
self.textinput.borrow_mut().edit_point.index = i as usize;
// trigger redraw
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
event.PreventDefault();
}
}
}
}
} else if event.type_() == atom!("keydown") && !event.DefaultPrevented() &&
(self.input_type.get() == InputType::InputText ||
self.input_type.get() == InputType::InputPassword) {

View file

@ -68,7 +68,7 @@ use script_layout_interface::message::{Msg, Reflow, ReflowQueryType, ScriptReflo
use script_layout_interface::reporter::CSSErrorReporter;
use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC};
use script_layout_interface::rpc::{MarginStyleResponse, NodeScrollRootIdResponse};
use script_layout_interface::rpc::ResolvedStyleResponse;
use script_layout_interface::rpc::{ResolvedStyleResponse, TextIndexResponse};
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptThreadEventCategory};
use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, Runnable, RunnableWrapper};
use script_thread::SendableMainThreadScriptChan;
@ -1362,6 +1362,15 @@ impl Window {
self.layout_rpc.margin_style()
}
pub fn text_index_query(&self, node: TrustedNodeAddress, mouse_x: i32, mouse_y: i32) -> TextIndexResponse {
if !self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::TextIndexQuery(node, mouse_x, mouse_y),
ReflowReason::Query) {
return TextIndexResponse(None);
}
self.layout_rpc.text_index()
}
#[allow(unsafe_code)]
pub fn init_browsing_context(&self, browsing_context: &BrowsingContext) {
assert!(self.browsing_context.get().is_none());
@ -1710,6 +1719,7 @@ fn debug_reflow_events(id: PipelineId, goal: &ReflowGoal, query_type: &ReflowQue
ReflowQueryType::ResolvedStyleQuery(_, _, _) => "\tResolvedStyleQuery",
ReflowQueryType::OffsetParentQuery(_n) => "\tOffsetParentQuery",
ReflowQueryType::MarginStyleQuery(_n) => "\tMarginStyleQuery",
ReflowQueryType::TextIndexQuery(..) => "\tTextIndexQuery",
});
debug_msg.push_str(match *reason {