slint/sixtyfps_runtime/corelib/item_rendering.rs
Simon Hausmann 628e6fdb38 Add a BoxShadow element
This intends to provide a configurable rectangular "drop shadow". The
API is modeled after CSS/HTML5 Canvas where the element can be "bound"
to an existing rectangular shape (geometry and radius), the offset can
be used to place the shadow and color and blur configure the shadow.

The shadow's color fades into transparent.

TODO (in subsequent changes):
 * Documentation
 * Qt implementation
2021-01-26 13:56:12 +01:00

130 lines
5.1 KiB
Rust

/* LICENSE BEGIN
This file is part of the SixtyFPS Project -- https://sixtyfps.io
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
SPDX-License-Identifier: GPL-3.0-only
This file is also available under commercial licensing terms.
Please contact info@sixtyfps.io for more information.
LICENSE END */
#![warn(missing_docs)]
//! module for rendering the tree of items
use super::graphics::RenderingCache;
use super::items::*;
use crate::component::ComponentRc;
use crate::graphics::Point;
use crate::item_tree::ItemVisitorResult;
use core::pin::Pin;
use std::cell::{Cell, RefCell};
/// This structure must be present in items that are Rendered and contains information.
/// Used by the backend.
#[derive(Default, Debug)]
#[repr(C)]
pub struct CachedRenderingData {
/// Used and modified by the backend, should be initialized to 0 by the user code
pub(crate) cache_index: Cell<usize>,
/// Set to false initially and when changes happen that require updating the cache
pub(crate) cache_ok: Cell<bool>,
}
impl CachedRenderingData {
/// This function allows retrieving the backend specific per-item data cache, updating
/// it if depending properties have changed. The supplied update_fn will be called when
/// properties have changed or the cache is initialized the first time.
pub fn ensure_up_to_date<T: Clone>(
&self,
cache: &mut RenderingCache<T>,
update_fn: impl FnOnce() -> T,
) -> T {
if self.cache_ok.get() {
let index = self.cache_index.get();
let existing_entry = cache.get_mut(index).unwrap();
if existing_entry.dependency_tracker.is_dirty() {
existing_entry.data = existing_entry.dependency_tracker.as_ref().evaluate(update_fn)
}
existing_entry.data.clone()
} else {
self.cache_index.set(cache.insert(crate::graphics::CachedGraphicsData::new(update_fn)));
self.cache_ok.set(true);
cache.get(self.cache_index.get()).unwrap().data.clone()
}
}
/// This function can be used to remove an entry from the rendering cache for a given item, if it
/// exists, i.e. if any data was ever cached. This is typically called by the graphics backend's
/// implementation of the release_item_graphics_cache function.
pub fn release<T>(&self, cache: &mut RenderingCache<T>) {
if self.cache_ok.get() {
let index = self.cache_index.get();
cache.remove(index);
}
}
}
/// Renders the tree of items that component holds, using the specified renderer. Rendering is done
/// relative to the specified origin.
pub fn render_component_items(
component: &ComponentRc,
renderer: &mut dyn ItemRenderer,
origin: crate::graphics::Point,
) {
let renderer = RefCell::new(renderer);
crate::item_tree::visit_items_with_post_visit(
component,
crate::item_tree::TraversalOrder::BackToFront,
|_, item, _, translation| {
renderer.borrow_mut().save_state();
item.as_ref()
.render(*translation, &mut (*renderer.borrow_mut() as &mut dyn ItemRenderer));
let origin = item.as_ref().geometry().origin;
let translation = *translation + euclid::Vector2D::new(origin.x, origin.y);
(ItemVisitorResult::Continue(translation), ())
},
|_, _, _| {
renderer.borrow_mut().restore_state();
},
origin,
);
}
/// Trait used to render each items.
///
/// The item needs to be rendered relative to its (x,y) position. For example,
/// draw_rectangle should draw a rectangle in `(pos.x + rect.x, pos.y + rect.y)`
#[allow(missing_docs)]
pub trait ItemRenderer {
fn draw_rectangle(&mut self, pos: Point, rect: Pin<&Rectangle>);
fn draw_border_rectangle(&mut self, pos: Point, rect: Pin<&BorderRectangle>);
fn draw_image(&mut self, pos: Point, image: Pin<&Image>);
fn draw_clipped_image(&mut self, pos: Point, image: Pin<&ClippedImage>);
fn draw_text(&mut self, pos: Point, text: Pin<&Text>);
fn draw_text_input(&mut self, pos: Point, text_input: Pin<&TextInput>);
fn draw_path(&mut self, pos: Point, path: Pin<&Path>);
fn draw_box_shadow(&mut self, pos: Point, box_shadow: Pin<&BoxShadow>);
fn combine_clip(&mut self, pos: Point, clip: Pin<&Clip>);
fn save_state(&mut self);
fn restore_state(&mut self);
/// Returns the scale factor
fn scale_factor(&self) -> f32;
/// Draw a pixmap in position indicated by the `pos`.
/// The pixmap will be taken from cache if the cache is valid, otherwise, update_fn will be called
/// with a callback that need to be called once with `fn (width, height, data)` where data are the
/// argb premultiplied pixel values
fn draw_cached_pixmap(
&mut self,
item_cache: &CachedRenderingData,
pos: Point,
update_fn: &dyn Fn(&mut dyn FnMut(u32, u32, &[u8])),
);
/// Return the internal renderer
fn as_any(&mut self) -> &mut dyn core::any::Any;
}