diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index 39d67aaf002..954b13e8014 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -128,6 +128,9 @@ fn run_main_loop(port: Port, let page_size = @mut Size2D(0f32, 0f32); let window_size = @mut Size2D(800, 600); + // Keeps track of the current zoom factor + let world_zoom = @mut 1f32; + let check_for_messages: @fn() = || { // Periodically check if the script task responded to our last resize event resize_rate_limiter.check_resize_response(); @@ -262,16 +265,65 @@ fn run_main_loop(port: Port, *world_offset = world_offset_copy - delta; // Clamp the world offset to the screen size. - let max_x = (page_size.width - window_size.width as f32).max(&0.0); + let max_x = (page_size.width * *world_zoom - window_size.width as f32).max(&0.0); world_offset.x = world_offset.x.clamp(&0.0, &max_x); - let max_y = (page_size.height - window_size.height as f32).max(&0.0); + let max_y = (page_size.height * *world_zoom - window_size.height as f32).max(&0.0); world_offset.y = world_offset.y.clamp(&0.0, &max_y); debug!("compositor: scrolled to %?", *world_offset); - root_layer.common.set_transform(identity().translate(-world_offset.x, - -world_offset.y, - 0.0)); + let mut scroll_transform = identity(); + + scroll_transform = scroll_transform.translate(window_size.width as f32 / 2f32 * *world_zoom - world_offset.x, + window_size.height as f32 / 2f32 * *world_zoom - world_offset.y, + 0.0); + scroll_transform = scroll_transform.scale(*world_zoom, *world_zoom, 1f32); + scroll_transform = scroll_transform.translate(window_size.width as f32 / -2f32, + window_size.height as f32 / -2f32, + 0.0); + + root_layer.common.set_transform(scroll_transform); + + window.set_needs_display() + } + + + + // When the user pinch-zooms, scale the layer + do window.set_zoom_callback |delta| { + let zoom_const = 0.01; + let old_world_zoom = *world_zoom; + + // Determine zoom amount + *world_zoom = (*world_zoom + delta.y * zoom_const).max(&1.0); + + // Update world offset + let corner_to_center_x = world_offset.x + window_size.width as f32 / 2f32; + let new_corner_to_center_x = corner_to_center_x * *world_zoom / old_world_zoom; + world_offset.x = world_offset.x + new_corner_to_center_x - corner_to_center_x; + + let corner_to_center_y = world_offset.y + window_size.height as f32 / 2f32; + let new_corner_to_center_y = corner_to_center_y * *world_zoom / old_world_zoom; + world_offset.y = world_offset.y + new_corner_to_center_y - corner_to_center_y; + + // Clamp to page bounds when zooming out + let max_x = (page_size.width * *world_zoom - window_size.width as f32).max(&0.0); + world_offset.x = world_offset.x.clamp(&0.0, &max_x); + let max_y = (page_size.height * *world_zoom - window_size.height as f32).max(&0.0); + world_offset.y = world_offset.y.clamp(&0.0, &max_y); + + + // Apply transformations + let mut zoom_transform = identity(); + zoom_transform = zoom_transform.translate(window_size.width as f32 / 2f32 * *world_zoom - world_offset.x, + window_size.height as f32 / 2f32 * *world_zoom - world_offset.y, + 0.0); + zoom_transform = zoom_transform.scale(*world_zoom, *world_zoom, 1f32); + zoom_transform = zoom_transform.translate(window_size.width as f32 / -2f32, + window_size.height as f32 / -2f32, + 0.0); + root_layer.common.set_transform(zoom_transform); + window.set_needs_display() } diff --git a/src/components/main/platform/common/glut_windowing.rs b/src/components/main/platform/common/glut_windowing.rs index 0af1a0295d8..d301dca365d 100644 --- a/src/components/main/platform/common/glut_windowing.rs +++ b/src/components/main/platform/common/glut_windowing.rs @@ -9,13 +9,13 @@ use windowing::{ApplicationMethods, CompositeCallback, LoadUrlCallback, MouseCallback}; use windowing::{ResizeCallback, ScrollCallback, WindowMethods, WindowMouseEvent, WindowClickEvent}; -use windowing::{WindowMouseDownEvent, WindowMouseUpEvent}; +use windowing::{WindowMouseDownEvent, WindowMouseUpEvent, ZoomCallback}; use alert::{Alert, AlertMethods}; use core::libc::c_int; use geom::point::Point2D; use geom::size::Size2D; -use glut::glut::{DOUBLE, WindowHeight, WindowWidth}; +use glut::glut::{ACTIVE_CTRL, DOUBLE, WindowHeight, WindowWidth}; use glut::glut; use glut::machack; @@ -39,8 +39,10 @@ pub struct Window { load_url_callback: Option, mouse_callback: Option, scroll_callback: Option, + zoom_callback: Option, drag_origin: Point2D, + mouse_down_button: @mut c_int, mouse_down_point: @mut Point2D, } @@ -61,12 +63,15 @@ impl WindowMethods for Window { load_url_callback: None, mouse_callback: None, scroll_callback: None, + zoom_callback: None, drag_origin: Point2D(0, 0), + mouse_down_button: @mut 0, mouse_down_point: @mut Point2D(0, 0), }; + // Register event handlers. do glut::reshape_func(window.glut_window) |width, height| { match window.resize_callback { @@ -132,6 +137,11 @@ impl WindowMethods for Window { self.scroll_callback = Some(new_scroll_callback) } + /// Registers a zoom to be run when the user zooms. + pub fn set_zoom_callback(&mut self, new_zoom_callback: ZoomCallback) { + self.zoom_callback = Some(new_zoom_callback) + } + /// Spins the event loop. pub fn check_loop(@mut self) { glut::check_loop() @@ -147,8 +157,19 @@ impl Window { /// Helper function to handle keyboard events. fn handle_key(&self, key: u8) { debug!("got key: %d", key as int); - if key == 12 { // ^L - self.load_url() + match key { + 12 => self.load_url(), // Ctrl+L + k if k == ('=' as u8) && (glut::get_modifiers() & ACTIVE_CTRL) != 0 => { // Ctrl++ + for self.zoom_callback.each |&callback| { + callback(Point2D(0.0, 20.0)); + } + } + k if k == 31 && (glut::get_modifiers() & ACTIVE_CTRL) != 0 => { // Ctrl+- + for self.zoom_callback.each |&callback| { + callback(Point2D(0.0, -20.0)); + } + } + _ => {} } } diff --git a/src/components/main/windowing.rs b/src/components/main/windowing.rs index 797f1445ceb..d3dd7b1bf0b 100644 --- a/src/components/main/windowing.rs +++ b/src/components/main/windowing.rs @@ -28,6 +28,9 @@ pub type MouseCallback = @fn(WindowMouseEvent); /// Type of the function that is called when the user scrolls. pub type ScrollCallback = @fn(Point2D); +///Type of the function that is called when the user zooms. +pub type ZoomCallback = @fn(Point2D); + /// Methods for an abstract Application. pub trait ApplicationMethods { fn new() -> Self; @@ -51,6 +54,8 @@ pub trait WindowMethods { pub fn set_mouse_callback(&mut self, new_mouse_callback: MouseCallback); /// Registers a callback to run when the user scrolls. pub fn set_scroll_callback(&mut self, new_scroll_callback: ScrollCallback); + /// Registers a callback to run when the user zooms. + pub fn set_zoom_callback(&mut self, new_zoom_callback: ZoomCallback); /// Spins the event loop. pub fn check_loop(@mut self);