Acord/viewport/src/block.rs

114 lines
3.7 KiB
Rust

//! 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<Message> {
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<InnerPath>;
/// 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<dyn Iterator<Item = InnerPath> + '_>;
}