mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-17 15:53:13 +00:00
565 lines
14 KiB
Rust
565 lines
14 KiB
Rust
mod mouse_event;
|
|
mod mouse_region;
|
|
mod region;
|
|
|
|
#[cfg(debug_assertions)]
|
|
use collections::HashSet;
|
|
use derive_more::Mul;
|
|
use schemars::JsonSchema;
|
|
use serde::Deserialize;
|
|
use serde_derive::Serialize;
|
|
use std::{
|
|
any::{Any, TypeId},
|
|
borrow::Cow,
|
|
rc::Rc,
|
|
sync::Arc,
|
|
};
|
|
|
|
use crate::{
|
|
color::Color,
|
|
fonts::{FontId, GlyphId},
|
|
geometry::{rect::RectF, vector::Vector2F},
|
|
platform::{current::Surface, CursorStyle},
|
|
ImageData, WindowContext,
|
|
};
|
|
pub use mouse_event::*;
|
|
pub use mouse_region::*;
|
|
|
|
pub struct SceneBuilder {
|
|
stacking_contexts: Vec<StackingContext>,
|
|
active_stacking_context_stack: Vec<usize>,
|
|
/// Used by the gpui2 crate.
|
|
pub event_handlers: Vec<EventHandler>,
|
|
#[cfg(debug_assertions)]
|
|
mouse_region_ids: HashSet<MouseRegionId>,
|
|
}
|
|
|
|
pub struct Scene {
|
|
scale_factor: f32,
|
|
stacking_contexts: Vec<StackingContext>,
|
|
event_handlers: Vec<EventHandler>,
|
|
}
|
|
|
|
struct StackingContext {
|
|
layers: Vec<Layer>,
|
|
active_layer_stack: Vec<usize>,
|
|
z_index: usize,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct Layer {
|
|
clip_bounds: Option<RectF>,
|
|
quads: Vec<Quad>,
|
|
underlines: Vec<Underline>,
|
|
images: Vec<Image>,
|
|
surfaces: Vec<Surface>,
|
|
shadows: Vec<Shadow>,
|
|
glyphs: Vec<Glyph>,
|
|
image_glyphs: Vec<ImageGlyph>,
|
|
icons: Vec<Icon>,
|
|
paths: Vec<Path>,
|
|
cursor_regions: Vec<CursorRegion>,
|
|
mouse_regions: Vec<MouseRegion>,
|
|
}
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub struct CursorRegion {
|
|
pub bounds: RectF,
|
|
pub style: CursorStyle,
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
pub struct Quad {
|
|
pub bounds: RectF,
|
|
pub background: Option<Color>,
|
|
pub border: Border,
|
|
pub corner_radii: CornerRadii,
|
|
}
|
|
|
|
#[derive(Default, Debug, Mul, Clone, Copy, Serialize, JsonSchema)]
|
|
pub struct CornerRadii {
|
|
pub top_left: f32,
|
|
pub top_right: f32,
|
|
pub bottom_right: f32,
|
|
pub bottom_left: f32,
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for CornerRadii {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
#[derive(Deserialize)]
|
|
pub struct CornerRadiiHelper {
|
|
pub top_left: Option<f32>,
|
|
pub top_right: Option<f32>,
|
|
pub bottom_right: Option<f32>,
|
|
pub bottom_left: Option<f32>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(untagged)]
|
|
enum RadiusOrRadii {
|
|
Radius(f32),
|
|
Radii(CornerRadiiHelper),
|
|
}
|
|
|
|
let json = RadiusOrRadii::deserialize(deserializer)?;
|
|
|
|
let result = match json {
|
|
RadiusOrRadii::Radius(radius) => CornerRadii::from(radius),
|
|
RadiusOrRadii::Radii(CornerRadiiHelper {
|
|
top_left,
|
|
top_right,
|
|
bottom_right,
|
|
bottom_left,
|
|
}) => CornerRadii {
|
|
top_left: top_left.unwrap_or(0.0),
|
|
top_right: top_right.unwrap_or(0.0),
|
|
bottom_right: bottom_right.unwrap_or(0.0),
|
|
bottom_left: bottom_left.unwrap_or(0.0),
|
|
},
|
|
};
|
|
|
|
Ok(result)
|
|
}
|
|
}
|
|
|
|
impl From<f32> for CornerRadii {
|
|
fn from(radius: f32) -> Self {
|
|
Self {
|
|
top_left: radius,
|
|
top_right: radius,
|
|
bottom_right: radius,
|
|
bottom_left: radius,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Shadow {
|
|
pub bounds: RectF,
|
|
pub corner_radii: CornerRadii,
|
|
pub sigma: f32,
|
|
pub color: Color,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Glyph {
|
|
pub font_id: FontId,
|
|
pub font_size: f32,
|
|
pub id: GlyphId,
|
|
pub origin: Vector2F,
|
|
pub color: Color,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ImageGlyph {
|
|
pub font_id: FontId,
|
|
pub font_size: f32,
|
|
pub id: GlyphId,
|
|
pub origin: Vector2F,
|
|
}
|
|
|
|
pub struct Icon {
|
|
pub bounds: RectF,
|
|
pub svg: usvg::Tree,
|
|
pub path: Cow<'static, str>,
|
|
pub color: Color,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Default, Debug)]
|
|
pub struct Border {
|
|
pub color: Color,
|
|
pub top: f32,
|
|
pub right: f32,
|
|
pub bottom: f32,
|
|
pub left: f32,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Default, Debug)]
|
|
pub struct Underline {
|
|
pub origin: Vector2F,
|
|
pub width: f32,
|
|
pub thickness: f32,
|
|
pub color: Color,
|
|
pub squiggly: bool,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Path {
|
|
pub bounds: RectF,
|
|
pub color: Color,
|
|
pub vertices: Vec<PathVertex>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct PathVertex {
|
|
pub xy_position: Vector2F,
|
|
pub st_position: Vector2F,
|
|
}
|
|
|
|
pub struct Image {
|
|
pub bounds: RectF,
|
|
pub border: Border,
|
|
pub corner_radii: CornerRadii,
|
|
pub grayscale: bool,
|
|
pub data: Arc<ImageData>,
|
|
}
|
|
|
|
impl Scene {
|
|
pub fn scale_factor(&self) -> f32 {
|
|
self.scale_factor
|
|
}
|
|
|
|
pub fn layers(&self) -> impl Iterator<Item = &Layer> {
|
|
self.stacking_contexts.iter().flat_map(|s| &s.layers)
|
|
}
|
|
|
|
pub fn cursor_regions(&self) -> Vec<CursorRegion> {
|
|
self.layers()
|
|
.flat_map(|layer| &layer.cursor_regions)
|
|
.copied()
|
|
.collect()
|
|
}
|
|
|
|
pub fn mouse_regions(&self) -> Vec<(MouseRegion, usize)> {
|
|
self.stacking_contexts
|
|
.iter()
|
|
.flat_map(|context| {
|
|
context
|
|
.layers
|
|
.iter()
|
|
.flat_map(|layer| &layer.mouse_regions)
|
|
.map(|region| (region.clone(), context.z_index))
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
pub fn take_event_handlers(&mut self) -> Vec<EventHandler> {
|
|
self.event_handlers
|
|
.sort_by(|a, b| a.order.cmp(&b.order).reverse());
|
|
std::mem::take(&mut self.event_handlers)
|
|
}
|
|
}
|
|
|
|
impl SceneBuilder {
|
|
pub fn new() -> Self {
|
|
let mut this = SceneBuilder {
|
|
stacking_contexts: Vec::new(),
|
|
active_stacking_context_stack: Vec::new(),
|
|
#[cfg(debug_assertions)]
|
|
mouse_region_ids: HashSet::default(),
|
|
event_handlers: Vec::new(),
|
|
};
|
|
this.clear();
|
|
this
|
|
}
|
|
|
|
pub fn clear(&mut self) {
|
|
self.stacking_contexts.clear();
|
|
self.stacking_contexts.push(StackingContext::new(None, 0));
|
|
self.active_stacking_context_stack.clear();
|
|
self.active_stacking_context_stack.push(0);
|
|
#[cfg(debug_assertions)]
|
|
self.mouse_region_ids.clear();
|
|
}
|
|
|
|
pub fn build(&mut self, scale_factor: f32) -> Scene {
|
|
let mut stacking_contexts = std::mem::take(&mut self.stacking_contexts);
|
|
stacking_contexts.sort_by_key(|context| context.z_index);
|
|
let event_handlers = std::mem::take(&mut self.event_handlers);
|
|
self.clear();
|
|
|
|
Scene {
|
|
scale_factor,
|
|
stacking_contexts,
|
|
event_handlers,
|
|
}
|
|
}
|
|
|
|
pub fn push_stacking_context(&mut self, clip_bounds: Option<RectF>, z_index: Option<usize>) {
|
|
let z_index = z_index.unwrap_or_else(|| self.active_stacking_context().z_index + 1);
|
|
self.active_stacking_context_stack
|
|
.push(self.stacking_contexts.len());
|
|
self.stacking_contexts
|
|
.push(StackingContext::new(clip_bounds, z_index))
|
|
}
|
|
|
|
pub fn pop_stacking_context(&mut self) {
|
|
self.active_stacking_context_stack.pop();
|
|
assert!(!self.active_stacking_context_stack.is_empty());
|
|
}
|
|
|
|
pub fn push_layer(&mut self, clip_bounds: Option<RectF>) {
|
|
self.active_stacking_context().push_layer(clip_bounds);
|
|
}
|
|
|
|
pub fn pop_layer(&mut self) {
|
|
self.active_stacking_context().pop_layer();
|
|
}
|
|
|
|
pub fn push_quad(&mut self, quad: Quad) {
|
|
self.active_layer().push_quad(quad)
|
|
}
|
|
|
|
pub fn push_cursor_region(&mut self, region: CursorRegion) {
|
|
if can_draw(region.bounds) {
|
|
self.active_layer().push_cursor_region(region);
|
|
}
|
|
}
|
|
|
|
pub fn push_mouse_region(&mut self, region: MouseRegion) {
|
|
if can_draw(region.bounds) {
|
|
// Ensure that Regions cannot be added to a scene with the same region id.
|
|
#[cfg(debug_assertions)]
|
|
let region_id;
|
|
#[cfg(debug_assertions)]
|
|
{
|
|
region_id = region.id();
|
|
}
|
|
|
|
if self.active_layer().push_mouse_region(region) {
|
|
#[cfg(debug_assertions)]
|
|
{
|
|
if !self.mouse_region_ids.insert(region_id) {
|
|
let tag_name = region_id.tag_type_name();
|
|
panic!("Same MouseRegionId: {region_id:?} inserted multiple times to the same scene. \
|
|
Will cause problems! Look for MouseRegion that uses Tag: {tag_name}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn push_image(&mut self, image: Image) {
|
|
self.active_layer().push_image(image)
|
|
}
|
|
|
|
pub fn push_surface(&mut self, surface: Surface) {
|
|
self.active_layer().push_surface(surface)
|
|
}
|
|
|
|
pub fn push_underline(&mut self, underline: Underline) {
|
|
self.active_layer().push_underline(underline)
|
|
}
|
|
|
|
pub fn push_shadow(&mut self, shadow: Shadow) {
|
|
self.active_layer().push_shadow(shadow)
|
|
}
|
|
|
|
pub fn push_glyph(&mut self, glyph: Glyph) {
|
|
self.active_layer().push_glyph(glyph)
|
|
}
|
|
|
|
pub fn push_image_glyph(&mut self, image_glyph: ImageGlyph) {
|
|
self.active_layer().push_image_glyph(image_glyph)
|
|
}
|
|
|
|
pub fn push_icon(&mut self, icon: Icon) {
|
|
self.active_layer().push_icon(icon)
|
|
}
|
|
|
|
pub fn push_path(&mut self, path: Path) {
|
|
self.active_layer().push_path(path);
|
|
}
|
|
|
|
fn active_stacking_context(&mut self) -> &mut StackingContext {
|
|
let ix = *self.active_stacking_context_stack.last().unwrap();
|
|
&mut self.stacking_contexts[ix]
|
|
}
|
|
|
|
fn active_layer(&mut self) -> &mut Layer {
|
|
self.active_stacking_context().active_layer()
|
|
}
|
|
}
|
|
|
|
impl StackingContext {
|
|
fn new(clip_bounds: Option<RectF>, z_index: usize) -> Self {
|
|
Self {
|
|
layers: vec![Layer::new(clip_bounds)],
|
|
active_layer_stack: vec![0],
|
|
z_index,
|
|
}
|
|
}
|
|
|
|
fn active_layer(&mut self) -> &mut Layer {
|
|
&mut self.layers[*self.active_layer_stack.last().unwrap()]
|
|
}
|
|
|
|
fn push_layer(&mut self, clip_bounds: Option<RectF>) {
|
|
let parent_clip_bounds = self.active_layer().clip_bounds();
|
|
let clip_bounds = clip_bounds
|
|
.map(|clip_bounds| {
|
|
clip_bounds
|
|
.intersection(parent_clip_bounds.unwrap_or(clip_bounds))
|
|
.unwrap_or_else(|| {
|
|
if !clip_bounds.is_empty() {
|
|
log::warn!("specified clip bounds are disjoint from parent layer");
|
|
}
|
|
RectF::default()
|
|
})
|
|
})
|
|
.or(parent_clip_bounds);
|
|
|
|
let ix = self.layers.len();
|
|
self.layers.push(Layer::new(clip_bounds));
|
|
self.active_layer_stack.push(ix);
|
|
}
|
|
|
|
fn pop_layer(&mut self) {
|
|
self.active_layer_stack.pop().unwrap();
|
|
assert!(!self.active_layer_stack.is_empty());
|
|
}
|
|
}
|
|
|
|
impl Layer {
|
|
pub fn new(clip_bounds: Option<RectF>) -> Self {
|
|
Self {
|
|
clip_bounds,
|
|
quads: Default::default(),
|
|
underlines: Default::default(),
|
|
images: Default::default(),
|
|
surfaces: Default::default(),
|
|
shadows: Default::default(),
|
|
image_glyphs: Default::default(),
|
|
glyphs: Default::default(),
|
|
icons: Default::default(),
|
|
paths: Default::default(),
|
|
cursor_regions: Default::default(),
|
|
mouse_regions: Default::default(),
|
|
}
|
|
}
|
|
|
|
pub fn clip_bounds(&self) -> Option<RectF> {
|
|
self.clip_bounds
|
|
}
|
|
|
|
fn push_quad(&mut self, quad: Quad) {
|
|
if can_draw(quad.bounds) {
|
|
self.quads.push(quad);
|
|
}
|
|
}
|
|
|
|
pub fn quads(&self) -> &[Quad] {
|
|
self.quads.as_slice()
|
|
}
|
|
|
|
fn push_cursor_region(&mut self, region: CursorRegion) {
|
|
if let Some(bounds) = region
|
|
.bounds
|
|
.intersection(self.clip_bounds.unwrap_or(region.bounds))
|
|
{
|
|
if can_draw(bounds) {
|
|
self.cursor_regions.push(region);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn push_mouse_region(&mut self, region: MouseRegion) -> bool {
|
|
if let Some(bounds) = region
|
|
.bounds
|
|
.intersection(self.clip_bounds.unwrap_or(region.bounds))
|
|
{
|
|
if can_draw(bounds) {
|
|
self.mouse_regions.push(region);
|
|
return true;
|
|
}
|
|
}
|
|
false
|
|
}
|
|
|
|
fn push_underline(&mut self, underline: Underline) {
|
|
if underline.width > 0. {
|
|
self.underlines.push(underline);
|
|
}
|
|
}
|
|
|
|
pub fn underlines(&self) -> &[Underline] {
|
|
self.underlines.as_slice()
|
|
}
|
|
|
|
fn push_image(&mut self, image: Image) {
|
|
if can_draw(image.bounds) {
|
|
self.images.push(image);
|
|
}
|
|
}
|
|
|
|
pub fn images(&self) -> &[Image] {
|
|
self.images.as_slice()
|
|
}
|
|
|
|
fn push_surface(&mut self, surface: Surface) {
|
|
if can_draw(surface.bounds) {
|
|
self.surfaces.push(surface);
|
|
}
|
|
}
|
|
|
|
pub fn surfaces(&self) -> &[Surface] {
|
|
self.surfaces.as_slice()
|
|
}
|
|
|
|
fn push_shadow(&mut self, shadow: Shadow) {
|
|
if can_draw(shadow.bounds) {
|
|
self.shadows.push(shadow);
|
|
}
|
|
}
|
|
|
|
pub fn shadows(&self) -> &[Shadow] {
|
|
self.shadows.as_slice()
|
|
}
|
|
|
|
fn push_image_glyph(&mut self, glyph: ImageGlyph) {
|
|
self.image_glyphs.push(glyph);
|
|
}
|
|
|
|
pub fn image_glyphs(&self) -> &[ImageGlyph] {
|
|
self.image_glyphs.as_slice()
|
|
}
|
|
|
|
fn push_glyph(&mut self, glyph: Glyph) {
|
|
self.glyphs.push(glyph);
|
|
}
|
|
|
|
pub fn glyphs(&self) -> &[Glyph] {
|
|
self.glyphs.as_slice()
|
|
}
|
|
|
|
pub fn push_icon(&mut self, icon: Icon) {
|
|
if can_draw(icon.bounds) {
|
|
self.icons.push(icon);
|
|
}
|
|
}
|
|
|
|
pub fn icons(&self) -> &[Icon] {
|
|
self.icons.as_slice()
|
|
}
|
|
|
|
fn push_path(&mut self, path: Path) {
|
|
if can_draw(path.bounds) {
|
|
self.paths.push(path);
|
|
}
|
|
}
|
|
|
|
pub fn paths(&self) -> &[Path] {
|
|
self.paths.as_slice()
|
|
}
|
|
}
|
|
|
|
impl MouseRegion {
|
|
pub fn id(&self) -> MouseRegionId {
|
|
self.id
|
|
}
|
|
}
|
|
|
|
pub struct EventHandler {
|
|
pub order: u32,
|
|
// The &dyn Any parameter below expects an event.
|
|
pub handler: Rc<dyn Fn(&dyn Any, &mut WindowContext) -> bool>,
|
|
pub event_type: TypeId,
|
|
}
|
|
|
|
fn can_draw(bounds: RectF) -> bool {
|
|
let size = bounds.size();
|
|
size.x() > 0. && size.y() > 0.
|
|
}
|