aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/components/gfx/display_list.rs2
-rw-r--r--src/components/main/compositing/mod.rs17
-rw-r--r--src/components/main/layout/layout_task.rs88
-rw-r--r--src/components/main/platform/common/glut_windowing.rs20
-rw-r--r--src/components/main/windowing.rs6
-rw-r--r--src/components/script/dom/event.rs5
-rw-r--r--src/components/script/layout_interface.rs7
-rw-r--r--src/components/script/script_task.rs23
8 files changed, 148 insertions, 20 deletions
diff --git a/src/components/gfx/display_list.rs b/src/components/gfx/display_list.rs
index 74c3bc0b45a..8a7d5135242 100644
--- a/src/components/gfx/display_list.rs
+++ b/src/components/gfx/display_list.rs
@@ -28,7 +28,7 @@ use std::arc;
/// A list of rendering operations to be performed.
pub struct DisplayList<E> {
- priv list: ~[DisplayItem<E>]
+ list: ~[DisplayItem<E>]
}
impl<E> DisplayList<E> {
diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs
index 63e4e55564b..c0b08be2f15 100644
--- a/src/components/main/compositing/mod.rs
+++ b/src/components/main/compositing/mod.rs
@@ -4,8 +4,9 @@
use compositing::resize_rate_limiter::ResizeRateLimiter;
use platform::{Application, Window};
-use script::script_task::{LoadMsg, ScriptMsg};
+use script::script_task::{LoadMsg, ScriptMsg, SendEventMsg};
use windowing::{ApplicationMethods, WindowMethods};
+use script::dom::event::ClickEvent;
use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods};
use core::cell::Cell;
@@ -222,10 +223,22 @@ fn run_main_loop(port: Port<Msg>,
resize_rate_limiter.window_resized(width, height)
}
+ let script_chan_clone = script_chan.clone();
+
// When the user enters a new URL, load it.
do window.set_load_url_callback |url_string| {
debug!("osmain: loading URL `%s`", url_string);
- script_chan.send(LoadMsg(url::make_url(url_string.to_str(), None)))
+ script_chan_clone.send(LoadMsg(url::make_url(url_string.to_str(), None)))
+ }
+
+ let script_chan_clone = script_chan.clone();
+
+ // When the user clicks, perform hit testing
+ do window.set_click_callback |layer_click_point| {
+ let world_click_point = layer_click_point + *world_offset;
+ debug!("osmain: clicked at %?", world_click_point);
+
+ script_chan_clone.send(SendEventMsg(ClickEvent(world_click_point)));
}
// When the user scrolls, move the layer around.
diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs
index a45d46e6df8..7034593fbe2 100644
--- a/src/components/main/layout/layout_task.rs
+++ b/src/components/main/layout/layout_task.rs
@@ -8,6 +8,7 @@
use css::matching::MatchMethods;
use css::select::new_css_select_ctx;
use layout::aux::{LayoutData, LayoutAuxMethods};
+use layout::box::RenderBox;
use layout::box_builder::LayoutTreeBuilder;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods};
@@ -32,15 +33,17 @@ use newcss::types::OriginAuthor;
use script::dom::event::ReflowEvent;
use script::dom::node::{AbstractNode, LayoutView};
use script::layout_interface::{AddStylesheetMsg, BuildData, BuildMsg, ContentBoxQuery};
-use script::layout_interface::{ContentBoxResponse, ContentBoxesQuery, ContentBoxesResponse};
-use script::layout_interface::{ExitMsg, LayoutQuery, LayoutResponse, LayoutTask};
-use script::layout_interface::{MatchSelectorsDamage, Msg, NoDamage, QueryMsg, ReflowDamage};
+use script::layout_interface::{HitTestQuery, ContentBoxResponse, HitTestResponse};
+use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery};
+use script::layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDamage, Msg, NoDamage};
+use script::layout_interface::{QueryMsg, ReflowDamage};
use script::script_task::{ScriptMsg, SendEventMsg};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::LocalImageCache;
use servo_util::tree::{TreeNodeRef, TreeUtils};
use servo_util::time::{ProfilerChan, profile, time};
use servo_util::time;
+use std::net::url::Url;
pub fn create_layout_task(render_task: RenderTask,
img_cache_task: ImageCacheTask,
@@ -67,6 +70,8 @@ struct Layout {
local_image_cache: @mut LocalImageCache,
from_script: Port<Msg>,
font_ctx: @mut FontContext,
+ doc_url: Option<Url>,
+ screen_size: Option<Size2D<Au>>,
/// This is used to root reader data.
layout_refs: ~[@mut LayoutData],
@@ -90,6 +95,9 @@ impl Layout {
local_image_cache: @mut LocalImageCache(image_cache_task),
from_script: from_script,
font_ctx: fctx,
+ doc_url: None,
+ screen_size: None,
+
layout_refs: ~[],
css_select_ctx: @mut new_css_select_ctx(),
profiler_chan: profiler_chan,
@@ -102,6 +110,21 @@ impl Layout {
}
}
+ // Create a layout context for use in building display lists, hit testing, &c.
+ fn build_layout_context(&self) -> LayoutContext {
+ let image_cache = self.local_image_cache;
+ let font_ctx = self.font_ctx;
+ let screen_size = self.screen_size.unwrap();
+ let doc_url = self.doc_url.clone();
+
+ LayoutContext {
+ image_cache: image_cache,
+ font_ctx: font_ctx,
+ doc_url: doc_url.unwrap(),
+ screen_size: Rect(Point2D(Au(0), Au(0)), screen_size),
+ }
+ }
+
fn handle_request(&mut self) -> bool {
match self.from_script.recv() {
AddStylesheetMsg(sheet) => self.handle_add_stylesheet(sheet),
@@ -147,20 +170,16 @@ impl Layout {
debug!("layout: damage is %?", data.damage);
debug!("layout: parsed Node tree");
debug!("%?", node.dump());
-
// Reset the image cache.
self.local_image_cache.next_round(self.make_on_image_available_cb(script_chan));
+ self.doc_url = Some(doc_url);
let screen_size = Size2D(Au::from_px(data.window_size.width as int),
Au::from_px(data.window_size.height as int));
+ self.screen_size = Some(screen_size);
// Create a layout context for use throughout the following passes.
- let mut layout_ctx = LayoutContext {
- image_cache: self.local_image_cache,
- font_ctx: self.font_ctx,
- doc_url: doc_url,
- screen_size: Rect(Point2D(Au(0), Au(0)), screen_size)
- };
+ let mut layout_ctx = self.build_layout_context();
// Initialize layout data for each node.
//
@@ -292,6 +311,55 @@ impl Layout {
reply_chan.send(response)
}
+ HitTestQuery(node, point) => {
+ // FIXME: Isolate this transmutation into a single "bridge" module.
+ let node: AbstractNode<LayoutView> = unsafe {
+ transmute(node)
+ };
+ let mut flow_node: AbstractNode<LayoutView> = node;
+ for node.traverse_preorder |node| {
+ if node.layout_data().flow.is_some() {
+ flow_node = node;
+ break;
+ }
+ };
+
+ let response = match flow_node.layout_data().flow {
+ None => {
+ debug!("HitTestQuery: flow is None");
+ Err(())
+ }
+ Some(flow) => {
+ let layout_ctx = self.build_layout_context();
+ let builder = DisplayListBuilder {
+ ctx: &layout_ctx,
+ };
+ let display_list: @Cell<DisplayList<RenderBox>> =
+ @Cell(DisplayList::new());
+ flow.build_display_list(&builder,
+ &flow.position(),
+ display_list);
+ // iterate in reverse to ensure we have the most recently painted render box
+ let (x, y) = (Au::from_frac_px(point.x as float),
+ Au::from_frac_px(point.y as float));
+ let mut resp = Err(());
+ let display_list = &display_list.take().list;
+ for display_list.each_reverse |display_item| {
+ let bounds = display_item.bounds();
+ if x <= bounds.origin.x + bounds.size.width &&
+ bounds.origin.x <= x &&
+ y < bounds.origin.y + bounds.size.height &&
+ bounds.origin.y < y {
+ resp = Ok(HitTestResponse(display_item.base().extra.node()));
+ break;
+ }
+ }
+ resp
+ }
+ };
+
+ reply_chan.send(response)
+ }
}
}
diff --git a/src/components/main/platform/common/glut_windowing.rs b/src/components/main/platform/common/glut_windowing.rs
index 8df2985425b..613064f2110 100644
--- a/src/components/main/platform/common/glut_windowing.rs
+++ b/src/components/main/platform/common/glut_windowing.rs
@@ -7,8 +7,8 @@
/// GLUT is a very old and bare-bones toolkit. However, it has good cross-platform support, at
/// least on desktops. It is designed for testing Servo without the need of a UI.
-use windowing::{ApplicationMethods, CompositeCallback, LoadUrlCallback, ResizeCallback};
-use windowing::{ScrollCallback, WindowMethods};
+use windowing::{ApplicationMethods, CompositeCallback, LoadUrlCallback, ClickCallback};
+use windowing::{ResizeCallback, ScrollCallback, WindowMethods};
use alert::{Alert, AlertMethods};
use core::libc::c_int;
@@ -35,6 +35,7 @@ pub struct Window {
composite_callback: Option<CompositeCallback>,
resize_callback: Option<ResizeCallback>,
load_url_callback: Option<LoadUrlCallback>,
+ click_callback: Option<ClickCallback>,
scroll_callback: Option<ScrollCallback>,
drag_origin: Point2D<c_int>,
@@ -54,6 +55,7 @@ impl WindowMethods<Application> for Window {
composite_callback: None,
resize_callback: None,
load_url_callback: None,
+ click_callback: None,
scroll_callback: None,
drag_origin: Point2D(0, 0),
@@ -77,6 +79,7 @@ impl WindowMethods<Application> for Window {
window.handle_key(key)
}
do glut::mouse_func |_, _, x, y| {
+ window.handle_click(x, y);
window.start_drag(x, y)
}
do glut::motion_func |x, y| {
@@ -111,6 +114,11 @@ impl WindowMethods<Application> for Window {
self.load_url_callback = Some(new_load_url_callback)
}
+ /// Registers a callback to be run when a click event occurs.
+ pub fn set_click_callback(&mut self, new_click_callback: ClickCallback) {
+ self.click_callback = Some(new_click_callback)
+ }
+
/// Registers a callback to be run when the user scrolls.
pub fn set_scroll_callback(&mut self, new_scroll_callback: ScrollCallback) {
self.scroll_callback = Some(new_scroll_callback)
@@ -136,6 +144,14 @@ impl Window {
}
}
+ /// Helper function to handle a click
+ fn handle_click(&self, x: c_int, y: c_int) {
+ match self.click_callback {
+ None => {}
+ Some(callback) => callback(Point2D(x as f32, y as f32)),
+ }
+ }
+
/// Helper function to start a drag.
fn start_drag(&mut self, x: c_int, y: c_int) {
self.drag_origin = Point2D(x, y)
diff --git a/src/components/main/windowing.rs b/src/components/main/windowing.rs
index 708ea4105ec..9a9cefaf03b 100644
--- a/src/components/main/windowing.rs
+++ b/src/components/main/windowing.rs
@@ -16,6 +16,10 @@ pub type ResizeCallback = @fn(uint, uint);
/// Type of the function that is called when a new URL is to be loaded.
pub type LoadUrlCallback = @fn(&str);
+/// Type of the function that is called when hit testing is to be performed.
+/// FIXME this currently does not discriminate between left and right clicks or any modifiers
+pub type ClickCallback = @fn(Point2D<f32>);
+
/// Type of the function that is called when the user scrolls.
pub type ScrollCallback = @fn(Point2D<f32>);
@@ -38,6 +42,8 @@ pub trait WindowMethods<A> {
pub fn set_resize_callback(&mut self, new_resize_callback: ResizeCallback);
/// Registers a callback to run when a new URL is to be loaded.
pub fn set_load_url_callback(&mut self, new_load_url_callback: LoadUrlCallback);
+ /// Registers a callback to run when the user clicks.
+ pub fn set_click_callback(&mut self, new_click_callback: ClickCallback);
/// Registers a callback to run when the user scrolls.
pub fn set_scroll_callback(&mut self, new_scroll_callback: ScrollCallback);
diff --git a/src/components/script/dom/event.rs b/src/components/script/dom/event.rs
index 7b1fba2ac37..a0ef39d8fca 100644
--- a/src/components/script/dom/event.rs
+++ b/src/components/script/dom/event.rs
@@ -7,9 +7,12 @@ use dom::window::Window;
use dom::bindings::codegen::EventBinding;
use dom::bindings::utils::{DOMString, ErrorResult, WrapperCache};
+use geom::point::Point2D;
+
pub enum Event {
ResizeEvent(uint, uint, comm::Chan<()>),
- ReflowEvent
+ ReflowEvent,
+ ClickEvent(Point2D<f32>),
}
pub struct Event_ {
diff --git a/src/components/script/layout_interface.rs b/src/components/script/layout_interface.rs
index b442dc440d3..86077244ecd 100644
--- a/src/components/script/layout_interface.rs
+++ b/src/components/script/layout_interface.rs
@@ -6,12 +6,13 @@
/// coupling between these two components, and enables the DOM to be placed in a separate crate
/// from layout.
-use dom::node::{AbstractNode, ScriptView};
+use dom::node::{AbstractNode, ScriptView, LayoutView};
use script_task::ScriptMsg;
use core::comm::{Chan, SharedChan};
use geom::rect::Rect;
use geom::size::Size2D;
+use geom::point::Point2D;
use gfx::geometry::Au;
use newcss::stylesheet::Stylesheet;
use std::net::url::Url;
@@ -43,6 +44,8 @@ pub enum LayoutQuery {
ContentBoxQuery(AbstractNode<ScriptView>),
/// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
ContentBoxesQuery(AbstractNode<ScriptView>),
+ /// Requests the node containing the point of interest
+ HitTestQuery(AbstractNode<ScriptView>, Point2D<f32>),
}
/// The reply of a synchronous message from script to layout.
@@ -54,6 +57,8 @@ pub enum LayoutResponse {
ContentBoxResponse(Rect<Au>),
/// A response to the `ContentBoxesQuery` message.
ContentBoxesResponse(~[Rect<Au>]),
+ /// A response to the `HitTestQuery` message.
+ HitTestResponse(AbstractNode<LayoutView>),
}
/// Dirty bits for layout.
diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs
index cf99428c677..094336bf1ab 100644
--- a/src/components/script/script_task.rs
+++ b/src/components/script/script_task.rs
@@ -7,11 +7,11 @@
use dom::bindings::utils::GlobalStaticData;
use dom::document::Document;
-use dom::event::{Event, ResizeEvent, ReflowEvent};
+use dom::event::{Event, ResizeEvent, ReflowEvent, ClickEvent};
use dom::node::define_bindings;
use dom::window::Window;
-use layout_interface::{AddStylesheetMsg, BuildData, BuildMsg, Damage, LayoutQuery};
-use layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDamage, NoDamage};
+use layout_interface::{AddStylesheetMsg, BuildData, BuildMsg, Damage, LayoutQuery, HitTestQuery};
+use layout_interface::{LayoutResponse, HitTestResponse, LayoutTask, MatchSelectorsDamage, NoDamage};
use layout_interface::{QueryMsg, ReflowDamage};
use layout_interface;
@@ -460,6 +460,23 @@ impl ScriptContext {
self.relayout()
}
}
+
+ ClickEvent(point) => {
+ debug!("ClickEvent: clicked at %?", point);
+ let root = match self.root_frame {
+ Some(ref frame) => frame.document.root,
+ None => fail!("root frame is None")
+ };
+ match self.query_layout(HitTestQuery(root, point)) {
+ Ok(node) => match node {
+ HitTestResponse(node) => debug!("clicked on %?", node.debug_str()),
+ _ => fail!(~"unexpected layout reply")
+ },
+ Err(()) => {
+ println(fmt!("layout query error"));
+ }
+ };
+ }
}
}
}