implement for painter

This commit is contained in:
charburgx 2022-12-17 18:27:10 -06:00
parent 1e770ae3bd
commit 4560618d43
4 changed files with 180 additions and 34 deletions

View file

@ -604,9 +604,9 @@ impl Context {
} }
/// Get a full-screen painter for a new or existing layer /// Get a full-screen painter for a new or existing layer
pub fn layer_painter(&self, layer_id: AreaLayerId) -> Painter { pub fn layer_painter(&self, layer: AreaLayerId) -> Painter {
let screen_rect = self.input().screen_rect(); let screen_rect = self.input().screen_rect();
Painter::new(self.clone(), layer_id, screen_rect) Painter::new(self.clone(), layer, screen_rect)
} }
/// Paint on top of everything else /// Paint on top of everything else

View file

@ -98,6 +98,11 @@ impl AreaLayerId {
self.order.allow_interaction() self.order.allow_interaction()
} }
#[must_use]
pub fn with_z(self, z: ZOrder) -> ZLayer {
ZLayer::from_area_layer_z(self, z)
}
/// Short and readable summary /// Short and readable summary
pub fn short_debug_format(&self) -> String { pub fn short_debug_format(&self) -> String {
format!( format!(
@ -108,13 +113,72 @@ impl AreaLayerId {
} }
} }
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct ZOrder(pub i32);
impl ZOrder {
const DEFAULT: ZOrder = ZOrder(0);
}
impl Default for ZOrder {
fn default() -> Self {
Self::DEFAULT
}
}
/// An identifier for a paint layer which supports Z-indexing
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct ZLayer {
pub area_layer: AreaLayerId,
pub z: ZOrder,
}
impl ZLayer {
pub fn new(order: Order, id: Id, z: ZOrder) -> Self {
Self {
area_layer: AreaLayerId { order, id },
z,
}
}
pub fn from_area_layer_z(area_layer: AreaLayerId, z: ZOrder) -> Self {
Self { area_layer, z }
}
pub fn from_area_layer(area_layer: AreaLayerId) -> Self {
Self::from_area_layer_z(area_layer, ZOrder::default())
}
pub fn debug() -> Self {
Self::from_area_layer(AreaLayerId::debug())
}
pub fn background() -> Self {
Self::from_area_layer(AreaLayerId::background())
}
}
/// A unique identifier of a specific [`Shape`] in a [`PaintList`]. /// A unique identifier of a specific [`Shape`] in a [`PaintList`].
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub struct ShapeIdx(usize); pub struct ShapeIdx(usize);
#[derive(Clone, PartialEq)]
struct PaintedShape {
shape: ClippedShape,
z: ZOrder,
}
impl PaintedShape {
pub fn new(shape: ClippedShape, z: ZOrder) -> Self {
Self { shape, z }
}
}
/// A list of [`Shape`]s paired with a clip rectangle. /// A list of [`Shape`]s paired with a clip rectangle.
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct PaintList(Vec<ClippedShape>); pub struct PaintList(Vec<PaintedShape>);
impl PaintList { impl PaintList {
#[inline(always)] #[inline(always)]
@ -124,17 +188,18 @@ impl PaintList {
/// Returns the index of the new [`Shape`] that can be used with `PaintList::set`. /// Returns the index of the new [`Shape`] that can be used with `PaintList::set`.
#[inline(always)] #[inline(always)]
pub fn add(&mut self, clip_rect: Rect, shape: Shape) -> ShapeIdx { pub fn add(&mut self, clip_rect: Rect, shape: Shape, z: ZOrder) -> ShapeIdx {
let idx = ShapeIdx(self.0.len()); let idx = ShapeIdx(self.0.len());
self.0.push(ClippedShape(clip_rect, shape)); self.0
.push(PaintedShape::new(ClippedShape(clip_rect, shape), z));
idx idx
} }
pub fn extend<I: IntoIterator<Item = Shape>>(&mut self, clip_rect: Rect, shapes: I) { pub fn extend<I: IntoIterator<Item = Shape>>(&mut self, clip_rect: Rect, shapes: I, z: ZOrder) {
self.0.extend( self.0.extend(
shapes shapes
.into_iter() .into_iter()
.map(|shape| ClippedShape(clip_rect, shape)), .map(|shape| PaintedShape::new(ClippedShape(clip_rect, shape), z)),
); );
} }
@ -147,12 +212,16 @@ impl PaintList {
/// and then later setting it using `paint_list.set(idx, cr, frame);`. /// and then later setting it using `paint_list.set(idx, cr, frame);`.
#[inline(always)] #[inline(always)]
pub fn set(&mut self, idx: ShapeIdx, clip_rect: Rect, shape: Shape) { pub fn set(&mut self, idx: ShapeIdx, clip_rect: Rect, shape: Shape) {
self.0[idx.0] = ClippedShape(clip_rect, shape); self.0[idx.0].shape = ClippedShape(clip_rect, shape);
} }
/// Translate each [`Shape`] and clip rectangle by this much, in-place /// Translate each [`Shape`] and clip rectangle by this much, in-place
pub fn translate(&mut self, delta: Vec2) { pub fn translate(&mut self, delta: Vec2) {
for ClippedShape(clip_rect, shape) in &mut self.0 { for PaintedShape {
shape: ClippedShape(clip_rect, shape),
..
} in &mut self.0
{
*clip_rect = clip_rect.translate(delta); *clip_rect = clip_rect.translate(delta);
shape.translate(delta); shape.translate(delta);
} }
@ -178,6 +247,11 @@ impl GraphicLayers {
for &order in &Order::ALL { for &order in &Order::ALL {
let order_map = &mut self.0[order as usize]; let order_map = &mut self.0[order as usize];
// Sort by z-order
for list in order_map.values_mut() {
list.0.sort_by_key(|PaintedShape { z, .. }| *z);
}
// If a layer is empty at the start of the frame // If a layer is empty at the start of the frame
// then nobody has added to it, and it is old and defunct. // then nobody has added to it, and it is old and defunct.
// Free it to save memory: // Free it to save memory:
@ -198,6 +272,8 @@ impl GraphicLayers {
} }
} }
all_shapes.into_iter() all_shapes
.into_iter()
.map(|PaintedShape { shape, .. }| shape)
} }
} }

View file

@ -3,8 +3,8 @@ use std::sync::Arc;
use crate::{ use crate::{
emath::{Align2, Pos2, Rect, Vec2}, emath::{Align2, Pos2, Rect, Vec2},
layers::{AreaLayerId, PaintList, ShapeIdx}, layers::{PaintList, ShapeIdx, ZLayer, ZOrder},
Color32, Context, FontId, AreaLayerId, Color32, Context, FontId,
}; };
use epaint::{ use epaint::{
mutex::{RwLockReadGuard, RwLockWriteGuard}, mutex::{RwLockReadGuard, RwLockWriteGuard},
@ -21,7 +21,7 @@ pub struct Painter {
ctx: Context, ctx: Context,
/// Where we paint /// Where we paint
layer_id: AreaLayerId, layer: ZLayer,
/// Everything painted in this [`Painter`] will be clipped against this. /// Everything painted in this [`Painter`] will be clipped against this.
/// This means nothing outside of this rectangle will be visible on screen. /// This means nothing outside of this rectangle will be visible on screen.
@ -34,10 +34,10 @@ pub struct Painter {
impl Painter { impl Painter {
/// Create a painter to a specific layer within a certain clip rectangle. /// Create a painter to a specific layer within a certain clip rectangle.
pub fn new(ctx: Context, layer_id: AreaLayerId, clip_rect: Rect) -> Self { pub fn new(ctx: Context, area_layer: AreaLayerId, clip_rect: Rect) -> Self {
Self { Self {
ctx, ctx,
layer_id, layer: ZLayer::from_area_layer(area_layer),
clip_rect, clip_rect,
fade_to_color: None, fade_to_color: None,
} }
@ -45,15 +45,28 @@ impl Painter {
/// Redirect where you are painting. /// Redirect where you are painting.
#[must_use] #[must_use]
pub fn with_layer_id(self, layer_id: AreaLayerId) -> Self { pub fn with_layer(self, layer: ZLayer) -> Self {
Self { Self {
ctx: self.ctx, ctx: self.ctx,
layer_id, layer,
clip_rect: self.clip_rect, clip_rect: self.clip_rect,
fade_to_color: None, fade_to_color: None,
} }
} }
/// Redirect where you are painting with default z-index
#[must_use]
pub fn with_layer_id(self, layer: AreaLayerId) -> Self {
self.with_layer(ZLayer::from_area_layer(layer))
}
/// Redirect z-index
#[must_use]
pub fn with_z(self, z: ZOrder) -> Self {
let layer = self.layer.area_layer.with_z(z);
self.with_layer(layer)
}
/// Create a painter for a sub-region of this [`Painter`]. /// Create a painter for a sub-region of this [`Painter`].
/// ///
/// The clip-rect of the returned [`Painter`] will be the intersection /// The clip-rect of the returned [`Painter`] will be the intersection
@ -61,15 +74,25 @@ impl Painter {
pub fn with_clip_rect(&self, rect: Rect) -> Self { pub fn with_clip_rect(&self, rect: Rect) -> Self {
Self { Self {
ctx: self.ctx.clone(), ctx: self.ctx.clone(),
layer_id: self.layer_id, layer: self.layer,
clip_rect: rect.intersect(self.clip_rect), clip_rect: rect.intersect(self.clip_rect),
fade_to_color: self.fade_to_color, fade_to_color: self.fade_to_color,
} }
} }
/// Redirect where you are painting. /// Redirect what area layer you are painting.
pub fn set_layer_id(&mut self, layer_id: AreaLayerId) { pub fn set_layer_id(&mut self, area_layer: AreaLayerId) {
self.layer_id = layer_id; self.layer.area_layer = area_layer;
}
/// Redirect at what z order you are drawing
pub fn set_z(&mut self, z: ZOrder) {
self.layer.z = z;
}
/// Redirect where you are drawing
pub fn set_layer(&mut self, layer: ZLayer) {
self.layer = layer;
} }
/// If set, colors will be modified to look like this /// If set, colors will be modified to look like this
@ -90,7 +113,7 @@ impl Painter {
pub fn sub_region(&self, rect: Rect) -> Self { pub fn sub_region(&self, rect: Rect) -> Self {
Self { Self {
ctx: self.ctx.clone(), ctx: self.ctx.clone(),
layer_id: self.layer_id, layer: self.layer,
clip_rect: rect.intersect(self.clip_rect), clip_rect: rect.intersect(self.clip_rect),
fade_to_color: self.fade_to_color, fade_to_color: self.fade_to_color,
} }
@ -113,8 +136,13 @@ impl Painter {
/// Where we paint /// Where we paint
#[inline(always)] #[inline(always)]
pub fn layer_id(&self) -> AreaLayerId { pub fn layer(&self) -> AreaLayerId {
self.layer_id self.layer.area_layer
}
#[inline(always)]
pub fn z(&self) -> ZOrder {
self.layer.z
} }
/// Everything painted in this [`Painter`] will be clipped against this. /// Everything painted in this [`Painter`] will be clipped against this.
@ -153,7 +181,7 @@ impl Painter {
/// ## Low level /// ## Low level
impl Painter { impl Painter {
fn paint_list(&self) -> RwLockWriteGuard<'_, PaintList> { fn paint_list(&self) -> RwLockWriteGuard<'_, PaintList> {
RwLockWriteGuard::map(self.ctx.graphics(), |g| g.list(self.layer_id)) RwLockWriteGuard::map(self.ctx.graphics(), |g| g.list(self.layer.area_layer))
} }
fn transform_shape(&self, shape: &mut Shape) { fn transform_shape(&self, shape: &mut Shape) {
@ -162,16 +190,29 @@ impl Painter {
} }
} }
fn add_to_paint_list(&self, shape: Shape) -> ShapeIdx {
self.paint_list().add(self.clip_rect, shape, self.layer.z)
}
fn extend_paint_list(&self, shapes: impl IntoIterator<Item = Shape>) {
self.paint_list()
.extend(self.clip_rect, shapes, self.layer.z);
}
fn set_shape_in_paint_list(&self, idx: ShapeIdx, shape: Shape) {
self.paint_list().set(idx, self.clip_rect, shape);
}
/// It is up to the caller to make sure there is room for this. /// It is up to the caller to make sure there is room for this.
/// Can be used for free painting. /// Can be used for free painting.
/// NOTE: all coordinates are screen coordinates! /// NOTE: all coordinates are screen coordinates!
pub fn add(&self, shape: impl Into<Shape>) -> ShapeIdx { pub fn add(&self, shape: impl Into<Shape>) -> ShapeIdx {
if self.fade_to_color == Some(Color32::TRANSPARENT) { if self.fade_to_color == Some(Color32::TRANSPARENT) {
self.paint_list().add(self.clip_rect, Shape::Noop) self.add_to_paint_list(Shape::Noop)
} else { } else {
let mut shape = shape.into(); let mut shape = shape.into();
self.transform_shape(&mut shape); self.transform_shape(&mut shape);
self.paint_list().add(self.clip_rect, shape) self.add_to_paint_list(shape)
} }
} }
@ -187,9 +228,9 @@ impl Painter {
self.transform_shape(&mut shape); self.transform_shape(&mut shape);
shape shape
}); });
self.paint_list().extend(self.clip_rect, shapes); self.extend_paint_list(shapes);
} else { } else {
self.paint_list().extend(self.clip_rect, shapes); self.extend_paint_list(shapes);
}; };
} }
@ -200,7 +241,7 @@ impl Painter {
} }
let mut shape = shape.into(); let mut shape = shape.into();
self.transform_shape(&mut shape); self.transform_shape(&mut shape);
self.paint_list().set(idx, self.clip_rect, shape); self.set_shape_in_paint_list(idx, shape);
} }
} }

View file

@ -6,8 +6,15 @@ use std::sync::Arc;
use epaint::mutex::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use epaint::mutex::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use crate::{ use crate::{
containers::*, ecolor::*, epaint::text::Fonts, layout::*, menu::MenuState, placer::Placer, containers::*,
widgets::*, *, ecolor::*,
epaint::text::Fonts,
layers::{ZLayer, ZOrder},
layout::*,
menu::MenuState,
placer::Placer,
widgets::*,
*,
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -317,7 +324,7 @@ impl Ui {
/// Use this to paint stuff within this [`Ui`]. /// Use this to paint stuff within this [`Ui`].
#[inline] #[inline]
pub fn layer_id(&self) -> AreaLayerId { pub fn layer_id(&self) -> AreaLayerId {
self.painter().layer_id() self.painter().layer()
} }
/// The [`InputState`] of the [`Context`] associated with this [`Ui`]. /// The [`InputState`] of the [`Context`] associated with this [`Ui`].
@ -1731,7 +1738,7 @@ impl Ui {
InnerResponse::new(ret, response) InnerResponse::new(ret, response)
} }
/// Redirect shapes to another paint layer. /// Redirect shapes to another area layer.
pub fn with_layer_id<R>( pub fn with_layer_id<R>(
&mut self, &mut self,
layer_id: AreaLayerId, layer_id: AreaLayerId,
@ -1743,6 +1750,28 @@ impl Ui {
}) })
} }
pub fn with_layer<R>(
&mut self,
layer: ZLayer,
add_contents: impl FnOnce(&mut Self) -> R,
) -> InnerResponse<R> {
self.scope(|ui| {
ui.painter.set_layer(layer);
add_contents(ui)
})
}
pub fn with_z<R>(
&mut self,
z: ZOrder,
add_contents: impl FnOnce(&mut Self) -> R,
) -> InnerResponse<R> {
self.scope(|ui| {
ui.painter.set_z(z);
add_contents(ui)
})
}
/// A [`CollapsingHeader`] that starts out collapsed. /// A [`CollapsingHeader`] that starts out collapsed.
pub fn collapsing<R>( pub fn collapsing<R>(
&mut self, &mut self,