mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Store pointer to Content in JSContext private slot; add methods for pure folding over a flow's boxes; Set up layout query protocol; Hook up HTMLImageElement.width to use content->layout blocking queries.
This commit is contained in:
parent
0b42b7f537
commit
b9b3895a8a
6 changed files with 248 additions and 109 deletions
|
@ -3,9 +3,10 @@ The content task is the main task that runs JavaScript and spawns layout
|
|||
tasks.
|
||||
*/
|
||||
|
||||
export ContentTask;
|
||||
export Content, ContentTask;
|
||||
export ControlMsg, ExecuteMsg, ParseMsg, ExitMsg, Timer;
|
||||
export PingMsg, PongMsg;
|
||||
export task_from_context;
|
||||
|
||||
use std::arc::{ARC, clone};
|
||||
use comm::{Port, Chan, listen, select2};
|
||||
|
@ -43,8 +44,8 @@ use task::{task, SingleThreaded};
|
|||
|
||||
use js::glue::bindgen::RUST_JSVAL_TO_OBJECT;
|
||||
use js::JSVAL_NULL;
|
||||
use js::jsapi::jsval;
|
||||
use js::jsapi::bindgen::JS_CallFunctionValue;
|
||||
use js::jsapi::{JSContext, jsval};
|
||||
use js::jsapi::bindgen::{JS_CallFunctionValue, JS_GetContextPrivate};
|
||||
use ptr::null;
|
||||
|
||||
enum ControlMsg {
|
||||
|
@ -65,25 +66,13 @@ fn ContentTask<S: Compositor Send Copy>(layout_task: LayoutTask,
|
|||
resource_task: ResourceTask,
|
||||
img_cache_task: ImageCacheTask) -> ContentTask {
|
||||
do task().sched_mode(SingleThreaded).spawn_listener::<ControlMsg> |from_master| {
|
||||
Content(layout_task, compositor, from_master, resource_task, img_cache_task).start();
|
||||
let content = Content(layout_task, from_master, resource_task, img_cache_task);
|
||||
compositor.add_event_listener(content.event_port.chan());
|
||||
content.start();
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a ping to layout and waits for the response
|
||||
#[allow(non_implicitly_copyable_typarams)]
|
||||
fn join_layout(scope: NodeScope, layout_task: LayoutTask) {
|
||||
|
||||
if scope.is_reader_forked() {
|
||||
listen(|response_from_layout| {
|
||||
layout_task.send(layout_task::PingMsg(response_from_layout));
|
||||
response_from_layout.recv();
|
||||
});
|
||||
scope.reader_joined();
|
||||
}
|
||||
}
|
||||
|
||||
struct Content<C:Compositor> {
|
||||
compositor: C,
|
||||
struct Content {
|
||||
layout_task: LayoutTask,
|
||||
image_cache_task: ImageCacheTask,
|
||||
from_master: comm::Port<ControlMsg>,
|
||||
|
@ -102,29 +91,26 @@ struct Content<C:Compositor> {
|
|||
compartment: Option<compartment>,
|
||||
}
|
||||
|
||||
fn Content<C:Compositor>(layout_task: LayoutTask,
|
||||
compositor: C,
|
||||
from_master: Port<ControlMsg>,
|
||||
resource_task: ResourceTask,
|
||||
img_cache_task: ImageCacheTask) -> Content<C> {
|
||||
|
||||
fn Content(layout_task: LayoutTask,
|
||||
from_master: Port<ControlMsg>,
|
||||
resource_task: ResourceTask,
|
||||
img_cache_task: ImageCacheTask) -> Content {
|
||||
|
||||
let jsrt = jsrt();
|
||||
let cx = jsrt.cx();
|
||||
let event_port = Port();
|
||||
|
||||
compositor.add_event_listener(event_port.chan());
|
||||
|
||||
cx.set_default_options_and_version();
|
||||
cx.set_logging_error_reporter();
|
||||
|
||||
let compartment = match cx.new_compartment(global_class) {
|
||||
Ok(c) => Some(c),
|
||||
Err(()) => None
|
||||
};
|
||||
|
||||
Content {
|
||||
let content = Content {
|
||||
layout_task : layout_task,
|
||||
image_cache_task : img_cache_task,
|
||||
compositor : compositor,
|
||||
from_master : from_master,
|
||||
event_port : event_port,
|
||||
|
||||
|
@ -138,10 +124,18 @@ fn Content<C:Compositor>(layout_task: LayoutTask,
|
|||
|
||||
resource_task : resource_task,
|
||||
compartment : compartment
|
||||
}
|
||||
};
|
||||
|
||||
cx.set_cx_private(ptr::to_unsafe_ptr(&content) as *());
|
||||
|
||||
content
|
||||
}
|
||||
|
||||
impl<C:Compositor> Content<C> {
|
||||
fn task_from_context(cx: *JSContext) -> &Content unsafe {
|
||||
cast::reinterpret_cast(&JS_GetContextPrivate(cx))
|
||||
}
|
||||
|
||||
impl Content {
|
||||
|
||||
fn start() {
|
||||
while self.handle_msg(select2(self.from_master, self.event_port)) {
|
||||
|
@ -159,7 +153,7 @@ impl<C:Compositor> Content<C> {
|
|||
fn handle_control_msg(control_msg: ControlMsg) -> bool {
|
||||
match control_msg {
|
||||
ParseMsg(url) => {
|
||||
#debug["content: Received url `%s` to parse", url_to_str(copy url)];
|
||||
debug!("content: Received url `%s` to parse", url_to_str(copy url));
|
||||
|
||||
// Note: we can parse the next document in parallel
|
||||
// with any previous documents.
|
||||
|
@ -174,9 +168,9 @@ impl<C:Compositor> Content<C> {
|
|||
let js_scripts = result.js_port.recv();
|
||||
|
||||
// Apply the css rules to the dom tree:
|
||||
#debug["css_rules: %?", css_rules];
|
||||
debug!("css_rules: %?", css_rules);
|
||||
|
||||
#debug["js_scripts: %?", js_scripts];
|
||||
debug!("js_scripts: %?", js_scripts);
|
||||
|
||||
let document = Document(root, self.scope, css_rules);
|
||||
let window = Window(self.from_master);
|
||||
|
@ -215,11 +209,11 @@ impl<C:Compositor> Content<C> {
|
|||
|
||||
|
||||
ExecuteMsg(url) => {
|
||||
#debug["content: Received url `%s` to execute", url_to_str(copy url)];
|
||||
debug!("content: Received url `%s` to execute", url_to_str(copy url));
|
||||
|
||||
match read_whole_file(&Path(url.path)) {
|
||||
Err(msg) => {
|
||||
println(#fmt["Error opening %s: %s", url_to_str(copy url), msg]);
|
||||
println(fmt!("Error opening %s: %s", url_to_str(copy url), msg));
|
||||
}
|
||||
Ok(bytes) => {
|
||||
let compartment = option::expect(self.compartment, ~"TODO error checking");
|
||||
|
@ -237,12 +231,31 @@ impl<C:Compositor> Content<C> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Sends a ping to layout and waits for the response (i.e., it has finished any
|
||||
pending layout request messages).
|
||||
*/
|
||||
fn join_layout() {
|
||||
if self.scope.is_reader_forked() {
|
||||
listen(|response_from_layout| {
|
||||
self.layout_task.send(layout_task::PingMsg(response_from_layout));
|
||||
response_from_layout.recv();
|
||||
});
|
||||
self.scope.reader_joined();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
This method will wait until the layout task has completed its current action,
|
||||
join the layout task, and then request a new layout run. It won't wait for the
|
||||
new layout computation to finish.
|
||||
*/
|
||||
fn relayout(document: Document, doc_url: &Url) {
|
||||
#debug("content: performing relayout");
|
||||
debug!("content: performing relayout");
|
||||
|
||||
// Now, join the layout so that they will see the latest
|
||||
// changes we have made.
|
||||
join_layout(self.scope, self.layout_task);
|
||||
self.join_layout();
|
||||
|
||||
// Send new document and relevant styles to layout
|
||||
// FIXME: Put CSS rules in an arc or something.
|
||||
|
@ -253,10 +266,23 @@ impl<C:Compositor> Content<C> {
|
|||
self.scope.reader_forked();
|
||||
}
|
||||
|
||||
fn query_layout(query: layout_task::LayoutQuery) -> layout_task::LayoutQueryResponse {
|
||||
self.relayout(*self.document.get(), &self.doc_url.get());
|
||||
self.join_layout();
|
||||
|
||||
let response_port = Port();
|
||||
self.layout_task.send(layout_task::QueryMsg(query, response_port.chan()));
|
||||
return response_port.recv()
|
||||
}
|
||||
|
||||
/**
|
||||
This is the main entry point for receiving and dispatching DOM events.
|
||||
*/
|
||||
// TODO: actually perform DOM event dispatch.
|
||||
fn handle_event(event: Event) -> bool {
|
||||
match event {
|
||||
ResizeEvent(new_width, new_height) => {
|
||||
#debug("content got resize event: %d, %d", new_width, new_height);
|
||||
debug!("content got resize event: %d, %d", new_width, new_height);
|
||||
match copy self.document {
|
||||
None => {
|
||||
// Nothing to do.
|
||||
|
@ -269,7 +295,7 @@ impl<C:Compositor> Content<C> {
|
|||
return true;
|
||||
}
|
||||
ReflowEvent => {
|
||||
#debug("content got reflow event");
|
||||
debug!("content got reflow event");
|
||||
match copy self.document {
|
||||
None => {
|
||||
// Nothing to do.
|
||||
|
|
|
@ -6,11 +6,13 @@ use js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, J
|
|||
use js::jsapi::{JSContext, jsval, JSObject, JSBool, jsid, JSClass, JSFreeOp, JSPropertySpec};
|
||||
use js::jsapi::bindgen::{JS_ValueToString, JS_GetStringCharsZAndLength, JS_ReportError,
|
||||
JS_GetReservedSlot, JS_SetReservedSlot, JS_NewStringCopyN,
|
||||
JS_DefineFunctions, JS_DefineProperty, JS_GetContextPrivate};
|
||||
JS_DefineFunctions, JS_DefineProperty};
|
||||
use js::jsapi::bindgen::*;
|
||||
use js::glue::bindgen::*;
|
||||
use js::crust::{JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ConvertStub};
|
||||
|
||||
use content::content_task::{Content, task_from_context};
|
||||
use layout::layout_task;
|
||||
use dom::node::{Node, NodeScope, Element};
|
||||
use dom::element::*;
|
||||
use node::NodeBundle;
|
||||
|
@ -70,20 +72,20 @@ extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut jsva
|
|||
}
|
||||
|
||||
let bundle = unwrap(obj);
|
||||
let width = (*bundle).payload.scope.write((*bundle).payload.node, |nd| {
|
||||
let node = (*bundle).payload.node;
|
||||
let scope = (*bundle).payload.scope;
|
||||
let width = scope.write(node, |nd| {
|
||||
match nd.kind {
|
||||
~Element(ed) => {
|
||||
match ed.kind {
|
||||
~HTMLImageElement(*) => {
|
||||
// TODO: this should actually come from rendered dimensions!
|
||||
match ed.get_attr(~"width") {
|
||||
Some(s) => match int::from_str(s) {
|
||||
Some(w) => au::from_px(w),
|
||||
None => au(0) /* failed to parse a number */
|
||||
},
|
||||
None => au(0) /* no width attr. */
|
||||
let content : &Content = task_from_context(cx);
|
||||
match content.query_layout(layout_task::ContentBox(node)) {
|
||||
Ok(rect) => rect.width,
|
||||
Err(()) => 0,
|
||||
}
|
||||
},
|
||||
// TODO: if nothing is being rendered(?), return zero dimensions
|
||||
}
|
||||
_ => fail ~"why is this not an image element?"
|
||||
}
|
||||
},
|
||||
|
@ -91,7 +93,7 @@ extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut jsva
|
|||
}
|
||||
});
|
||||
*vp = RUST_INT_TO_JSVAL(
|
||||
(au::to_px(width) & (i32::max_value as int)) as libc::c_int);
|
||||
(width & (i32::max_value as int)) as libc::c_int);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ pub struct ImageHolder {
|
|||
// occasionally while get_image is being called
|
||||
mut url : Option<Url>,
|
||||
mut image : Option<ARC<~Image>>,
|
||||
mut cached_size: Size2D<int>,
|
||||
image_cache_task: ImageCacheTask,
|
||||
reflow_cb: fn~(),
|
||||
|
||||
|
@ -23,6 +24,7 @@ fn ImageHolder(url : &Url, image_cache_task: ImageCacheTask, cb: fn~()) -> Image
|
|||
let holder = ImageHolder {
|
||||
url : Some(copy *url),
|
||||
image : None,
|
||||
cached_size : Size2D(0,0),
|
||||
image_cache_task : image_cache_task,
|
||||
reflow_cb : copy cb,
|
||||
};
|
||||
|
@ -39,13 +41,26 @@ fn ImageHolder(url : &Url, image_cache_task: ImageCacheTask, cb: fn~()) -> Image
|
|||
}
|
||||
|
||||
impl ImageHolder {
|
||||
/**
|
||||
This version doesn't perform any computation, but may be stale w.r.t.
|
||||
newly-available image data that determines size.
|
||||
|
||||
The intent is that the impure version is used during layout when
|
||||
dimensions are used for computing layout.
|
||||
*/
|
||||
pure fn size() -> Size2D<int> {
|
||||
self.cached_size
|
||||
}
|
||||
|
||||
/** Query and update current image size */
|
||||
fn get_size() -> Option<Size2D<int>> {
|
||||
debug!("get_size() %?", self.url);
|
||||
match self.get_image() {
|
||||
Some(img) => {
|
||||
let img_ref = get(&img);
|
||||
Some(Size2D(img_ref.width as int,
|
||||
img_ref.height as int))
|
||||
self.cached_size = Size2D(img_ref.width as int,
|
||||
img_ref.height as int);
|
||||
Some(copy self.cached_size)
|
||||
},
|
||||
None => None
|
||||
}
|
||||
|
|
|
@ -183,10 +183,10 @@ impl @RenderBox {
|
|||
|
||||
/* The box formed by the content edge, as defined in CSS 2.1 Section 8.1.
|
||||
Coordinates are relative to the owning flow. */
|
||||
fn content_box() -> Rect<au> {
|
||||
pure fn content_box() -> Rect<au> {
|
||||
match self.kind {
|
||||
ImageBox(i) => {
|
||||
let size = i.get_size().get_default(Size2D(0,0));
|
||||
let size = i.size();
|
||||
Rect {
|
||||
origin: copy self.data.position.origin,
|
||||
size: Size2D(au::from_px(size.width),
|
||||
|
|
|
@ -140,7 +140,25 @@ impl @FlowContext {
|
|||
|
||||
// Actual methods that do not require much flow-specific logic
|
||||
impl @FlowContext {
|
||||
fn iter_boxes_for_node<T>(node: Node, cb: pure fn&(@RenderBox) -> T) {
|
||||
pure fn foldl_boxes_for_node<B: Copy>(node: Node, seed: B, blk: pure fn&(B,@RenderBox) -> B) -> B {
|
||||
match self.kind {
|
||||
RootFlow(d) => match d.box {
|
||||
Some(box) if box.node == node => { blk(seed, box) },
|
||||
_ => seed
|
||||
},
|
||||
BlockFlow(d) => match d.box {
|
||||
Some(box) if box.node == node => { blk(seed, box) },
|
||||
_ => seed
|
||||
},
|
||||
InlineFlow(d) => do d.boxes.foldl(seed) |acc, box| {
|
||||
if box.node == node { blk(acc, box) }
|
||||
else { acc }
|
||||
},
|
||||
_ => fail fmt!("Don't know how to iterate node's RenderBoxes for %?", self.kind)
|
||||
}
|
||||
}
|
||||
|
||||
pure fn iter_boxes_for_node<T>(node: Node, cb: pure fn&(@RenderBox) -> T) {
|
||||
match self.kind {
|
||||
RootFlow(d) => match d.box {
|
||||
Some(box) if box.node == node => { cb(box); },
|
||||
|
|
|
@ -19,6 +19,7 @@ use gfx::render_task;
|
|||
use layout::box::RenderBox;
|
||||
use layout::box_builder::LayoutTreeBuilder;
|
||||
use layout::context::LayoutContext;
|
||||
use opt = core::option;
|
||||
use render_task::RenderTask;
|
||||
use resource::image_cache_task::ImageCacheTask;
|
||||
use servo_text::font_cache::FontCache;
|
||||
|
@ -31,72 +32,149 @@ use task::*;
|
|||
|
||||
type LayoutTask = Chan<Msg>;
|
||||
|
||||
enum LayoutQuery {
|
||||
ContentBox(Node)
|
||||
}
|
||||
|
||||
type LayoutQueryResponse = Result<LayoutQueryResponse_, ()>;
|
||||
|
||||
enum LayoutQueryResponse_ {
|
||||
ContentSize(Size2D<int>)
|
||||
}
|
||||
|
||||
enum Msg {
|
||||
BuildMsg(Node, ARC<Stylesheet>, Url, Chan<Event>),
|
||||
PingMsg(Chan<content_task::PingMsg>),
|
||||
QueryMsg(LayoutQuery, Chan<LayoutQueryResponse>),
|
||||
ExitMsg
|
||||
}
|
||||
|
||||
fn LayoutTask(render_task: RenderTask, image_cache_task: ImageCacheTask) -> LayoutTask {
|
||||
do spawn_listener::<Msg>|request| {
|
||||
fn LayoutTask(render_task: RenderTask,
|
||||
img_cache_task: ImageCacheTask) -> LayoutTask {
|
||||
do spawn_listener::<Msg>|from_content| {
|
||||
Layout(render_task, img_cache_task, from_content).start();
|
||||
}
|
||||
}
|
||||
|
||||
// This just keeps our dom aux objects alive
|
||||
let layout_data_refs = DVec();
|
||||
let font_cache = FontCache();
|
||||
struct Layout {
|
||||
render_task: RenderTask,
|
||||
image_cache_task: ImageCacheTask,
|
||||
from_content: comm::Port<Msg>,
|
||||
|
||||
loop {
|
||||
match request.recv() {
|
||||
PingMsg(ping_channel) => ping_channel.send(content_task::PongMsg),
|
||||
ExitMsg => {
|
||||
debug!("layout: ExitMsg received");
|
||||
break;
|
||||
}
|
||||
BuildMsg(node, styles, doc_url, event_chan) => {
|
||||
debug!("layout: received layout request for: %s", doc_url.to_str());
|
||||
debug!("layout: parsed Node tree");
|
||||
node.dump();
|
||||
font_cache: @FontCache,
|
||||
// This is used to root auxilliary RCU reader data
|
||||
layout_refs: DVec<@LayoutData>
|
||||
}
|
||||
|
||||
let layout_ctx = LayoutContext {
|
||||
image_cache: image_cache_task,
|
||||
font_cache: font_cache,
|
||||
doc_url: doc_url,
|
||||
reflow_cb: || event_chan.send(ReflowEvent),
|
||||
// TODO: obtain screen size from a real data source
|
||||
screen_size: Rect(Point2D(au(0), au(0)), Size2D(au::from_px(800), au::from_px(600)))
|
||||
};
|
||||
fn Layout(render_task: RenderTask,
|
||||
image_cache_task: ImageCacheTask,
|
||||
from_content: comm::Port<Msg>) -> Layout {
|
||||
|
||||
do util::time::time(~"layout") {
|
||||
// TODO: this is dumb. we don't need 3 separate traversals.
|
||||
node.initialize_style_for_subtree(&layout_ctx, &layout_data_refs);
|
||||
node.recompute_style_for_subtree(&layout_ctx, styles);
|
||||
/* resolve styles (convert relative values) down the node tree */
|
||||
apply_style(&layout_ctx, node, layout_ctx.reflow_cb);
|
||||
Layout {
|
||||
render_task: render_task,
|
||||
image_cache_task: image_cache_task,
|
||||
from_content: from_content,
|
||||
font_cache: FontCache(),
|
||||
layout_refs: DVec()
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
|
||||
fn handle_query(query: LayoutQuery,
|
||||
reply_chan: comm::Chan<LayoutQueryResponse>) {
|
||||
match query {
|
||||
ContentBox(node) => {
|
||||
// TODO: extract me to a method when I get sibling arms
|
||||
let response = match node.aux(|a| a).flow {
|
||||
None => Err(()),
|
||||
Some(flow) => {
|
||||
let start_val : Option<Rect<au>> = None;
|
||||
let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| {
|
||||
match acc {
|
||||
Some(acc) => Some(acc.union(&box.content_box())),
|
||||
None => Some(box.content_box())
|
||||
}
|
||||
};
|
||||
|
||||
let builder = LayoutTreeBuilder();
|
||||
let layout_root: @FlowContext = match builder.construct_trees(&layout_ctx, node) {
|
||||
Ok(root) => root,
|
||||
Err(*) => fail ~"Root flow should always exist"
|
||||
};
|
||||
|
||||
debug!("layout: constructed Flow tree");
|
||||
layout_root.dump();
|
||||
|
||||
/* perform layout passes over the flow tree */
|
||||
do layout_root.traverse_postorder |f| { f.bubble_widths(&layout_ctx) }
|
||||
do layout_root.traverse_preorder |f| { f.assign_widths(&layout_ctx) }
|
||||
do layout_root.traverse_postorder |f| { f.assign_height(&layout_ctx) }
|
||||
|
||||
let dlist = DVec();
|
||||
let builder = dl::DisplayListBuilder {
|
||||
ctx: &layout_ctx,
|
||||
};
|
||||
// TODO: set options on the builder before building
|
||||
// TODO: be smarter about what needs painting
|
||||
layout_root.build_display_list(&builder, © layout_root.data.position, &dlist);
|
||||
render_task.send(render_task::RenderMsg(dlist));
|
||||
match rect {
|
||||
None => Err(()),
|
||||
Some(rect) => {
|
||||
let size = Size2D(au::to_px(rect.size.width),
|
||||
au::to_px(rect.size.height));
|
||||
Ok(ContentSize(move size))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
reply_chan.send(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start() {
|
||||
while self.handle_request(self.from_content) {
|
||||
// loop indefinitely
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_request(request: comm::Port<Msg>) -> bool {
|
||||
match request.recv() {
|
||||
PingMsg(ping_channel) => ping_channel.send(content_task::PongMsg),
|
||||
QueryMsg(query, chan) => self.handle_query(query, chan),
|
||||
ExitMsg => {
|
||||
debug!("layout: ExitMsg received");
|
||||
return false
|
||||
},
|
||||
BuildMsg(node, styles, doc_url, to_content) => {
|
||||
debug!("layout: received layout request for: %s", doc_url.to_str());
|
||||
debug!("layout: parsed Node tree");
|
||||
node.dump();
|
||||
|
||||
let layout_ctx = LayoutContext {
|
||||
image_cache: self.image_cache_task,
|
||||
font_cache: self.font_cache,
|
||||
doc_url: doc_url,
|
||||
reflow_cb: || to_content.send(ReflowEvent),
|
||||
// TODO: obtain screen size from a real data source
|
||||
screen_size: Rect(Point2D(au(0), au(0)), Size2D(au::from_px(800), au::from_px(600)))
|
||||
};
|
||||
|
||||
do util::time::time(~"layout") {
|
||||
// TODO: this is dumb. we don't need 3 separate traversals.
|
||||
node.initialize_style_for_subtree(&layout_ctx, &self.layout_refs);
|
||||
node.recompute_style_for_subtree(&layout_ctx, styles);
|
||||
/* resolve styles (convert relative values) down the node tree */
|
||||
apply_style(&layout_ctx, node, layout_ctx.reflow_cb);
|
||||
|
||||
let builder = LayoutTreeBuilder();
|
||||
let layout_root: @FlowContext = match builder.construct_trees(&layout_ctx, node) {
|
||||
Ok(root) => root,
|
||||
Err(*) => fail ~"Root flow should always exist"
|
||||
};
|
||||
|
||||
debug!("layout: constructed Flow tree");
|
||||
layout_root.dump();
|
||||
|
||||
/* perform layout passes over the flow tree */
|
||||
do layout_root.traverse_postorder |f| { f.bubble_widths(&layout_ctx) }
|
||||
do layout_root.traverse_preorder |f| { f.assign_widths(&layout_ctx) }
|
||||
do layout_root.traverse_postorder |f| { f.assign_height(&layout_ctx) }
|
||||
|
||||
let dlist = DVec();
|
||||
let builder = dl::DisplayListBuilder {
|
||||
ctx: &layout_ctx,
|
||||
};
|
||||
// TODO: set options on the builder before building
|
||||
// TODO: be smarter about what needs painting
|
||||
layout_root.build_display_list(&builder, © layout_root.data.position, &dlist);
|
||||
self.render_task.send(render_task::RenderMsg(dlist));
|
||||
} // time(layout)
|
||||
} // BuildMsg
|
||||
} // match
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue