implement for painter
This commit is contained in:
parent
1e770ae3bd
commit
4560618d43
4 changed files with 180 additions and 34 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue