use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use crate::draw_ctx::DrawCtx;
use crate::event::{Event, EventResult, Key, Modifiers, MouseButton};
use crate::framebuffer::Framebuffer;
use crate::geometry::{Point, Rect, Size};
use crate::gfx_ctx::GfxCtx;
use crate::layout_props::{HAnchor, Insets, VAnchor};
use crate::lcd_coverage::LcdBuffer;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BackbufferMode {
Rgba,
LcdCoverage,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BackbufferKind {
None,
SoftwareRgba,
SoftwareLcd,
GlFbo,
}
#[derive(Clone, Copy, Debug)]
pub struct BackbufferSpec {
pub kind: BackbufferKind,
pub cached: bool,
pub alpha: f64,
pub outsets: Insets,
pub rounded_clip: Option<f64>,
}
impl BackbufferSpec {
pub const fn none() -> Self {
Self {
kind: BackbufferKind::None,
cached: false,
alpha: 1.0,
outsets: Insets::ZERO,
rounded_clip: None,
}
}
}
impl Default for BackbufferSpec {
fn default() -> Self {
Self::none()
}
}
pub struct BackbufferCache {
pub pixels: Option<Arc<Vec<u8>>>,
pub lcd_alpha: Option<Arc<Vec<u8>>>,
pub width: u32,
pub height: u32,
pub dirty: bool,
pub theme_epoch: u64,
pub typography_epoch: u64,
}
impl BackbufferCache {
pub fn new() -> Self {
Self {
pixels: None,
lcd_alpha: None,
width: 0,
height: 0,
dirty: true,
theme_epoch: 0,
typography_epoch: 0,
}
}
pub fn invalidate(&mut self) {
self.dirty = true;
}
}
impl Default for BackbufferCache {
fn default() -> Self {
Self::new()
}
}
static NEXT_BACKBUFFER_ID: AtomicU64 = AtomicU64::new(1);
pub struct BackbufferState {
id: u64,
pub cache: BackbufferCache,
pub dirty: bool,
pub width: u32,
pub height: u32,
pub spec_kind: BackbufferKind,
pub theme_epoch: u64,
pub typography_epoch: u64,
pub repaint_count: u64,
pub composite_count: u64,
}
impl BackbufferState {
pub fn new() -> Self {
Self {
id: NEXT_BACKBUFFER_ID.fetch_add(1, Ordering::Relaxed),
cache: BackbufferCache::new(),
dirty: true,
width: 0,
height: 0,
spec_kind: BackbufferKind::None,
theme_epoch: 0,
typography_epoch: 0,
repaint_count: 0,
composite_count: 0,
}
}
pub fn id(&self) -> u64 {
self.id
}
pub fn invalidate(&mut self) {
self.dirty = true;
self.cache.invalidate();
}
}
impl Default for BackbufferState {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug)]
pub struct CompositingLayer {
pub outset_left: f64,
pub outset_bottom: f64,
pub outset_right: f64,
pub outset_top: f64,
pub alpha: f64,
}
impl CompositingLayer {
pub const fn new(
outset_left: f64,
outset_bottom: f64,
outset_right: f64,
outset_top: f64,
alpha: f64,
) -> Self {
Self {
outset_left,
outset_bottom,
outset_right,
outset_top,
alpha,
}
}
}
pub trait Widget {
fn bounds(&self) -> Rect;
fn set_bounds(&mut self, bounds: Rect);
fn children(&self) -> &[Box<dyn Widget>];
fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>>;
fn layout(&mut self, available: Size) -> Size;
fn paint(&mut self, ctx: &mut dyn DrawCtx);
fn hit_test(&self, local_pos: Point) -> bool {
let b = self.bounds();
local_pos.x >= 0.0
&& local_pos.x <= b.width
&& local_pos.y >= 0.0
&& local_pos.y <= b.height
}
fn claims_pointer_exclusively(&self, _local_pos: Point) -> bool {
false
}
fn hit_test_global_overlay(&self, _local_pos: Point) -> bool {
false
}
fn has_active_modal(&self) -> bool {
false
}
fn on_event(&mut self, event: &Event) -> EventResult;
fn on_unconsumed_key(&mut self, _key: &Key, _modifiers: Modifiers) -> EventResult {
EventResult::Ignored
}
fn is_focusable(&self) -> bool {
false
}
fn type_name(&self) -> &'static str {
"Widget"
}
fn id(&self) -> Option<&str> {
None
}
fn is_visible(&self) -> bool {
true
}
fn properties(&self) -> Vec<(&'static str, String)> {
vec![]
}
fn has_backbuffer(&self) -> bool {
false
}
fn compositing_layer(&mut self) -> Option<CompositingLayer> {
None
}
fn backbuffer_spec(&mut self) -> BackbufferSpec {
let mode = self.backbuffer_mode();
if self.backbuffer_cache_mut().is_some() {
BackbufferSpec {
kind: match mode {
BackbufferMode::Rgba => BackbufferKind::SoftwareRgba,
BackbufferMode::LcdCoverage => BackbufferKind::SoftwareLcd,
},
cached: true,
alpha: 1.0,
outsets: Insets::ZERO,
rounded_clip: None,
}
} else {
BackbufferSpec::none()
}
}
fn backbuffer_state_mut(&mut self) -> Option<&mut BackbufferState> {
None
}
fn mark_dirty(&mut self) {
if let Some(state) = self.backbuffer_state_mut() {
state.invalidate();
}
}
fn backbuffer_cache_mut(&mut self) -> Option<&mut BackbufferCache> {
None
}
fn backbuffer_mode(&self) -> BackbufferMode {
BackbufferMode::Rgba
}
fn contributes_children_to_inspector(&self) -> bool {
true
}
fn show_in_inspector(&self) -> bool {
true
}
fn lcd_preference(&self) -> Option<bool> {
None
}
fn paint_overlay(&mut self, _ctx: &mut dyn DrawCtx) {}
fn finish_paint(&mut self, _ctx: &mut dyn DrawCtx) {}
fn paint_global_overlay(&mut self, _ctx: &mut dyn DrawCtx) {}
fn clip_children_rect(&self) -> Option<(f64, f64, f64, f64)> {
let b = self.bounds();
Some((0.0, 0.0, b.width, b.height))
}
fn margin(&self) -> Insets {
Insets::ZERO
}
fn h_anchor(&self) -> HAnchor {
HAnchor::FIT
}
fn v_anchor(&self) -> VAnchor {
VAnchor::FIT
}
fn min_size(&self) -> Size {
Size::ZERO
}
fn max_size(&self) -> Size {
Size::MAX
}
fn enforce_integer_bounds(&self) -> bool {
crate::pixel_bounds::default_enforce_integer_bounds()
}
fn measure_min_height(&self, _available_w: f64) -> f64 {
self.min_size().height
}
fn take_raise_request(&mut self) -> bool {
false
}
fn needs_draw(&self) -> bool {
if !self.is_visible() {
return false;
}
self.children().iter().any(|c| c.needs_draw())
}
fn next_draw_deadline(&self) -> Option<web_time::Instant> {
if !self.is_visible() {
return None;
}
let mut best: Option<web_time::Instant> = None;
for c in self.children() {
if let Some(t) = c.next_draw_deadline() {
best = Some(match best {
Some(b) if b <= t => b,
_ => t,
});
}
}
best
}
}
mod app;
mod paint;
mod tree;
pub use app::App;
pub use paint::{current_paint_clip, paint_global_overlays, paint_subtree};
pub use tree::{
active_modal_path, collect_inspector_nodes, current_mouse_world, current_viewport,
dispatch_event, dispatch_unconsumed_key, find_widget_by_id, find_widget_by_id_mut,
find_widget_by_type, global_overlay_hit_path, hit_test_subtree, set_current_mouse_world,
set_current_viewport, InspectorNode,
};