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
pub fn layer_painter(&self, layer_id: AreaLayerId) -> Painter {
pub fn layer_painter(&self, layer: AreaLayerId) -> Painter {
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

View file

@ -98,6 +98,11 @@ impl AreaLayerId {
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
pub fn short_debug_format(&self) -> String {
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`].
#[derive(Clone, Copy, PartialEq, Eq)]
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.
#[derive(Clone, Default)]
pub struct PaintList(Vec<ClippedShape>);
pub struct PaintList(Vec<PaintedShape>);
impl PaintList {
#[inline(always)]
@ -124,17 +188,18 @@ impl PaintList {
/// Returns the index of the new [`Shape`] that can be used with `PaintList::set`.
#[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());
self.0.push(ClippedShape(clip_rect, shape));
self.0
.push(PaintedShape::new(ClippedShape(clip_rect, shape), z));
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(
shapes
.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);`.
#[inline(always)]
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
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);
shape.translate(delta);
}
@ -178,6 +247,11 @@ impl GraphicLayers {
for &order in &Order::ALL {
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
// then nobody has added to it, and it is old and defunct.
// 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::{
emath::{Align2, Pos2, Rect, Vec2},
layers::{AreaLayerId, PaintList, ShapeIdx},
Color32, Context, FontId,
layers::{PaintList, ShapeIdx, ZLayer, ZOrder},
AreaLayerId, Color32, Context, FontId,
};
use epaint::{
mutex::{RwLockReadGuard, RwLockWriteGuard},
@ -21,7 +21,7 @@ pub struct Painter {
ctx: Context,
/// Where we paint
layer_id: AreaLayerId,
layer: ZLayer,
/// Everything painted in this [`Painter`] will be clipped against this.
/// This means nothing outside of this rectangle will be visible on screen.
@ -34,10 +34,10 @@ pub struct Painter {
impl Painter {
/// 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 {
ctx,
layer_id,
layer: ZLayer::from_area_layer(area_layer),
clip_rect,
fade_to_color: None,
}
@ -45,15 +45,28 @@ impl Painter {
/// Redirect where you are painting.
#[must_use]
pub fn with_layer_id(self, layer_id: AreaLayerId) -> Self {
pub fn with_layer(self, layer: ZLayer) -> Self {
Self {
ctx: self.ctx,
layer_id,
layer,
clip_rect: self.clip_rect,
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`].
///
/// 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 {
Self {
ctx: self.ctx.clone(),
layer_id: self.layer_id,
layer: self.layer,
clip_rect: rect.intersect(self.clip_rect),
fade_to_color: self.fade_to_color,
}
}
/// Redirect where you are painting.
pub fn set_layer_id(&mut self, layer_id: AreaLayerId) {
self.layer_id = layer_id;
/// Redirect what area layer you are painting.
pub fn set_layer_id(&mut self, area_layer: AreaLayerId) {
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
@ -90,7 +113,7 @@ impl Painter {
pub fn sub_region(&self, rect: Rect) -> Self {
Self {
ctx: self.ctx.clone(),
layer_id: self.layer_id,
layer: self.layer,
clip_rect: rect.intersect(self.clip_rect),
fade_to_color: self.fade_to_color,
}
@ -113,8 +136,13 @@ impl Painter {
/// Where we paint
#[inline(always)]
pub fn layer_id(&self) -> AreaLayerId {
self.layer_id
pub fn layer(&self) -> AreaLayerId {
self.layer.area_layer
}
#[inline(always)]
pub fn z(&self) -> ZOrder {
self.layer.z
}
/// Everything painted in this [`Painter`] will be clipped against this.
@ -153,7 +181,7 @@ impl Painter {
/// ## Low level
impl Painter {
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) {
@ -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.
/// Can be used for free painting.
/// NOTE: all coordinates are screen coordinates!
pub fn add(&self, shape: impl Into<Shape>) -> ShapeIdx {
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 {
let mut shape = shape.into();
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);
shape
});
self.paint_list().extend(self.clip_rect, shapes);
self.extend_paint_list(shapes);
} 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();
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 crate::{
containers::*, ecolor::*, epaint::text::Fonts, layout::*, menu::MenuState, placer::Placer,
widgets::*, *,
containers::*,
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`].
#[inline]
pub fn layer_id(&self) -> AreaLayerId {
self.painter().layer_id()
self.painter().layer()
}
/// The [`InputState`] of the [`Context`] associated with this [`Ui`].
@ -1731,7 +1738,7 @@ impl Ui {
InnerResponse::new(ret, response)
}
/// Redirect shapes to another paint layer.
/// Redirect shapes to another area layer.
pub fn with_layer_id<R>(
&mut self,
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.
pub fn collapsing<R>(
&mut self,