//! block trait and layered view compositor interface. use iced_wgpu::core::{Element, Point, Theme}; use crate::text_widget; use crate::selection::{BlockId, InnerPath, NodePath, Selection}; use crate::table_block::TableMessage; /// z-ordering layers for the document compositor. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Layer { /// cursorline highlights, selection background tints. Below = 0, /// structural body of the block. Content = 1, /// selection borders, focus rings, eval result tags. Above = 2, /// drag previews, ghosted reorder anchors. Floating = 3, } /// layered output from a block's view, merged by the compositor in layer order. pub struct LayeredView<'a, Message> { /// main structural element, rendered in document order. pub base: Element<'a, Message, Theme, iced_wgpu::Renderer>, /// overlays tagged with their target layer. pub overlays: Vec<(Layer, Element<'a, Message, Theme, iced_wgpu::Renderer>)>, } impl<'a, Message> LayeredView<'a, Message> { /// constructs a LayeredView with no overlays. pub fn just(base: Element<'a, Message, Theme, iced_wgpu::Renderer>) -> Self { Self { base, overlays: Vec::new() } } } /// shared rendering context passed into every Block::view call. pub struct ViewCtx<'a, Message: 'a> { pub block_index: usize, pub selection: &'a Selection, pub focus: Option<&'a NodePath>, pub editing: Option<&'a NodePath>, pub font_size: f32, pub is_dark: bool, pub on_text_action: fn(usize, text_widget::Action) -> Message, pub on_table_msg: fn(usize, TableMessage) -> Message, /// evaluated formula results keyed by (block id, col, row). pub computed_cells: &'a std::collections::HashMap< (BlockId, u32, u32), acord_core::interp::Value, >, } /// structural mutation commands routed to a specific block by the editor. #[derive(Debug, Clone)] pub enum BlockCommand { // Table commands InsertRowAbove(usize), InsertRowBelow(usize), DeleteRow(usize), InsertColLeft(usize), InsertColRight(usize), DeleteCol(usize), SetCellValue { row: usize, col: usize, value: String }, ResizeCol { col: usize, width: f32 }, ResizeRow { row: usize, height: f32 }, MoveCol { from: usize, to: usize }, MoveRow { from: usize, to: usize }, // Heading commands SetHeadingLevel(u8), SetHeadingText(String), // Generic SelectAll, Clear, } /// protocol every block kind implements, generic over the editor's message type. pub trait Block { fn id(&self) -> BlockId; fn kind_tag(&self) -> &'static str; /// document-relative line where the block begins. fn start_line(&self) -> usize; fn set_start_line(&mut self, line: usize); /// number of lines the block contributes to the document. fn line_count(&self) -> usize; /// marks blocks produced by eval rather than parsed from markdown. fn is_eval_result(&self) -> bool { false } /// downcast to concrete block types. fn as_any(&self) -> &dyn std::any::Any; fn as_any_mut(&mut self) -> &mut dyn std::any::Any; /// renders the block into layered output for the compositor. fn view<'a>(&'a self, ctx: &ViewCtx<'_, Message>) -> LayeredView<'a, Message>; /// serializes the block as plain markdown. fn to_md(&self) -> String; /// resolves a local-space point to the innermost selectable path. fn hit_test(&self, point: Point) -> Option; /// applies a structural mutation, ignoring unsupported commands. fn apply(&mut self, cmd: BlockCommand); /// iterates all selectable inner paths in the block (non-recursive). fn selectable_paths(&self) -> Box + '_>; }