From efdf435ba3b9334c15466a765257443b554aebfc Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 15 Sep 2015 16:32:39 -0700 Subject: [PATCH] gfx: Paint the insertion point as a one-pixel wide line. --- components/layout/display_list_builder.rs | 65 ++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 9a78c0d8909..bafe3d2be10 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -34,6 +34,7 @@ use gfx::display_list::{GradientStop, ImageDisplayItem, LineDisplayItem}; use gfx::display_list::{OpaqueNode, SolidColorDisplayItem}; use gfx::display_list::{StackingContext, TextDisplayItem, TextOrientation}; use gfx::paint_task::THREAD_TINT_COLORS; +use gfx::text::glyph::CharIndex; use gfx_traits::color; use ipc_channel::ipc::{self, IpcSharedMemory}; use msg::compositor_msg::{ScrollPolicy, LayerId}; @@ -60,15 +61,19 @@ use style::values::computed::{LengthOrNone, LengthOrPercentage, LengthOrPercenta use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirection}; use url::Url; use util::cursor::Cursor; -use util::geometry::{Au, ZERO_POINT}; +use util::geometry::{AU_PER_PX, Au, ZERO_POINT}; use util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode}; use util::opts; +use util::range::Range; /// The fake fragment ID we use to indicate the inner display list for `overflow: scroll`. /// /// FIXME(pcwalton): This is pretty ugly. Consider modifying `LayerId` somehow. const FAKE_FRAGMENT_ID_FOR_OVERFLOW_SCROLL: u32 = 1000000; +/// The logical width of an insertion point: at the moment, a one-pixel-wide line. +const INSERTION_POINT_LOGICAL_WIDTH: Au = Au(1 * AU_PER_PX); + /// Whether a stacking context needs a layer or not. pub enum StackingContextLayerNecessity { Always(LayerId, ScrollPolicy), @@ -229,6 +234,14 @@ pub trait FragmentDisplayListBuilding { stacking_relative_border_box: &Rect) -> ClippingRegion; + /// Builds the display items necessary to paint the selection and/or caret for this fragment, + /// if any. + fn build_display_items_for_selection_if_necessary(&self, + display_list: &mut DisplayList, + stacking_relative_border_box: &Rect, + level: StackingLevel, + clip: &ClippingRegion); + /// Creates the text display item for one text fragment. This can be called multiple times for /// one fragment if there are text shadows. /// @@ -882,6 +895,50 @@ impl FragmentDisplayListBuilding for Fragment { (*parent_clip).clone().intersect_rect(&Rect::new(clip_origin, clip_size)) } + fn build_display_items_for_selection_if_necessary(&self, + display_list: &mut DisplayList, + stacking_relative_border_box: &Rect, + level: StackingLevel, + clip: &ClippingRegion) { + let scanned_text_fragment_info = match self.specific { + SpecificFragmentInfo::ScannedText(ref scanned_text_fragment_info) => { + scanned_text_fragment_info + } + _ => return, + }; + let insertion_point_index = match scanned_text_fragment_info.insertion_point { + Some(insertion_point_index) => insertion_point_index, + None => return, + }; + let range = Range::new(CharIndex(0), insertion_point_index); + let advance = scanned_text_fragment_info.run.advance_for_range(&range); + + let insertion_point_bounds; + let cursor; + if !self.style.writing_mode.is_vertical() { + insertion_point_bounds = + Rect::new(Point2D::new(stacking_relative_border_box.origin.x + advance, + stacking_relative_border_box.origin.y), + Size2D::new(INSERTION_POINT_LOGICAL_WIDTH, + stacking_relative_border_box.size.height)); + cursor = Cursor::TextCursor; + } else { + insertion_point_bounds = + Rect::new(Point2D::new(stacking_relative_border_box.origin.x, + stacking_relative_border_box.origin.y + advance), + Size2D::new(stacking_relative_border_box.size.width, + INSERTION_POINT_LOGICAL_WIDTH)); + cursor = Cursor::VerticalTextCursor; + }; + + display_list.push(DisplayItem::SolidColorClass(box SolidColorDisplayItem { + base: BaseDisplayItem::new(insertion_point_bounds, + DisplayItemMetadata::new(self.node, &*self.style, cursor), + clip.clone()), + color: self.style().get_color().color.to_gfx_color(), + }), level); + } + fn build_display_list(&mut self, display_list: &mut DisplayList, layout_context: &LayoutContext, @@ -991,6 +1048,12 @@ impl FragmentDisplayListBuilding for Fragment { &stacking_relative_border_box, &clip); } + + // Paint the selection point if necessary. + self.build_display_items_for_selection_if_necessary(display_list, + &stacking_relative_border_box, + level, + &clip); } // Create special per-fragment-type display items.