1use std::collections::HashSet;
4use std::sync::Arc;
5
6use parking_lot::Mutex;
7
8use frontend::commands::{block_commands, frame_commands, table_cell_commands, table_commands};
9use frontend::common::types::EntityId;
10
11use crate::FrameFormat;
12use crate::convert::to_usize;
13use crate::flow::{CellSnapshot, FlowElement, FlowElementSnapshot, FrameSnapshot, TableSnapshot};
14use crate::inner::TextDocumentInner;
15use crate::text_block::TextBlock;
16use crate::text_table::TextTable;
17
18#[derive(Clone)]
22pub struct TextFrame {
23 pub(crate) doc: Arc<Mutex<TextDocumentInner>>,
24 pub(crate) frame_id: usize,
25}
26
27impl TextFrame {
28 pub fn id(&self) -> usize {
30 self.frame_id
31 }
32
33 pub fn format(&self) -> FrameFormat {
35 let inner = self.doc.lock();
36 let frame_dto = frame_commands::get_frame(&inner.ctx, &(self.frame_id as EntityId))
37 .ok()
38 .flatten();
39 match frame_dto {
40 Some(f) => frame_dto_to_format(&f),
41 None => FrameFormat::default(),
42 }
43 }
44
45 pub fn flow(&self) -> Vec<FlowElement> {
48 let inner = self.doc.lock();
49 build_flow_elements(&inner, &self.doc, self.frame_id as EntityId)
50 }
51
52 pub fn snapshot(&self) -> FrameSnapshot {
56 let inner = self.doc.lock();
57 let format = frame_commands::get_frame(&inner.ctx, &(self.frame_id as EntityId))
58 .ok()
59 .flatten()
60 .map(|f| frame_dto_to_format(&f))
61 .unwrap_or_default();
62 let elements = build_flow_snapshot(&inner, self.frame_id as EntityId);
63 FrameSnapshot {
64 frame_id: self.frame_id,
65 format,
66 elements,
67 }
68 }
69}
70
71pub(crate) fn build_flow_elements(
80 inner: &TextDocumentInner,
81 doc_arc: &Arc<Mutex<TextDocumentInner>>,
82 frame_id: EntityId,
83) -> Vec<FlowElement> {
84 let frame_dto = match frame_commands::get_frame(&inner.ctx, &frame_id)
85 .ok()
86 .flatten()
87 {
88 Some(f) => f,
89 None => return Vec::new(),
90 };
91
92 if !frame_dto.child_order.is_empty() {
93 flow_from_child_order(inner, doc_arc, &frame_dto.child_order)
94 } else {
95 flow_fallback(inner, doc_arc, &frame_dto)
96 }
97}
98
99fn flow_from_child_order(
101 inner: &TextDocumentInner,
102 doc_arc: &Arc<Mutex<TextDocumentInner>>,
103 child_order: &[i64],
104) -> Vec<FlowElement> {
105 let mut elements = Vec::with_capacity(child_order.len());
106
107 for &entry in child_order {
108 if entry > 0 {
109 elements.push(FlowElement::Block(TextBlock {
111 doc: Arc::clone(doc_arc),
112 block_id: entry as usize,
113 }));
114 } else if entry < 0 {
115 let sub_frame_id = (-entry) as EntityId;
117 if let Some(sub_frame) = frame_commands::get_frame(&inner.ctx, &sub_frame_id)
118 .ok()
119 .flatten()
120 {
121 if let Some(table_id) = sub_frame.table {
122 elements.push(FlowElement::Table(TextTable {
124 doc: Arc::clone(doc_arc),
125 table_id: table_id as usize,
126 }));
127 } else {
128 elements.push(FlowElement::Frame(TextFrame {
130 doc: Arc::clone(doc_arc),
131 frame_id: sub_frame_id as usize,
132 }));
133 }
134 }
135 }
136 }
138
139 elements
140}
141
142fn flow_fallback(
144 inner: &TextDocumentInner,
145 doc_arc: &Arc<Mutex<TextDocumentInner>>,
146 frame_dto: &frontend::frame::dtos::FrameDto,
147) -> Vec<FlowElement> {
148 let cell_frame_ids = build_cell_frame_ids(inner);
150
151 let block_ids = &frame_dto.blocks;
153 let mut block_dtos: Vec<_> = block_ids
154 .iter()
155 .filter_map(|&id| {
156 block_commands::get_block(&inner.ctx, &{ id })
157 .ok()
158 .flatten()
159 })
160 .collect();
161 block_dtos.sort_by_key(|b| b.document_position);
162
163 let mut elements: Vec<FlowElement> = block_dtos
164 .iter()
165 .map(|b| {
166 FlowElement::Block(TextBlock {
167 doc: Arc::clone(doc_arc),
168 block_id: b.id as usize,
169 })
170 })
171 .collect();
172
173 let all_frames = frame_commands::get_all_frame(&inner.ctx).unwrap_or_default();
178 for f in &all_frames {
179 if f.id == frame_dto.id {
180 continue; }
182 if cell_frame_ids.contains(&(f.id as EntityId)) {
183 continue; }
185 if f.parent_frame == Some(frame_dto.id) {
187 if let Some(table_id) = f.table {
188 elements.push(FlowElement::Table(TextTable {
189 doc: Arc::clone(doc_arc),
190 table_id: table_id as usize,
191 }));
192 } else {
193 elements.push(FlowElement::Frame(TextFrame {
194 doc: Arc::clone(doc_arc),
195 frame_id: f.id as usize,
196 }));
197 }
198 }
199 }
200
201 elements
202}
203
204fn build_cell_frame_ids(inner: &TextDocumentInner) -> HashSet<EntityId> {
206 let mut ids = HashSet::new();
207 let all_cells = table_cell_commands::get_all_table_cell(&inner.ctx).unwrap_or_default();
208 for cell in &all_cells {
209 if let Some(frame_id) = cell.cell_frame {
210 ids.insert(frame_id);
211 }
212 }
213 ids
214}
215
216pub(crate) fn build_flow_snapshot(
226 inner: &TextDocumentInner,
227 frame_id: EntityId,
228) -> Vec<FlowElementSnapshot> {
229 let frame_dto = match frame_commands::get_frame(&inner.ctx, &frame_id)
230 .ok()
231 .flatten()
232 {
233 Some(f) => f,
234 None => return Vec::new(),
235 };
236
237 if !frame_dto.child_order.is_empty() {
238 let (elements, _) = snapshot_from_child_order(inner, &frame_dto.child_order, 0);
239 elements
240 } else {
241 snapshot_fallback(inner, &frame_dto)
242 }
243}
244
245fn snapshot_from_child_order(
248 inner: &TextDocumentInner,
249 child_order: &[i64],
250 start_pos: usize,
251) -> (Vec<FlowElementSnapshot>, usize) {
252 let mut elements = Vec::with_capacity(child_order.len());
253 let mut running_pos = start_pos;
254
255 for &entry in child_order {
256 if entry > 0 {
257 let block_id = entry as u64;
258 if let Some(snap) = crate::text_block::build_block_snapshot_with_position(
259 inner,
260 block_id,
261 Some(running_pos),
262 ) {
263 running_pos += snap.length + 1; elements.push(FlowElementSnapshot::Block(snap));
265 }
266 } else if entry < 0 {
267 let sub_frame_id = (-entry) as EntityId;
268 if let Some(sub_frame) = frame_commands::get_frame(&inner.ctx, &sub_frame_id)
269 .ok()
270 .flatten()
271 {
272 if let Some(table_id) = sub_frame.table {
273 if let Some(snap) = build_table_snapshot(inner, table_id) {
274 elements.push(FlowElementSnapshot::Table(snap));
278 }
279 } else {
280 let (nested, new_pos) =
281 snapshot_from_child_order(inner, &sub_frame.child_order, running_pos);
282 running_pos = new_pos;
283 elements.push(FlowElementSnapshot::Frame(FrameSnapshot {
284 frame_id: sub_frame_id as usize,
285 format: frame_dto_to_format(&sub_frame),
286 elements: nested,
287 }));
288 }
289 }
290 }
291 }
292
293 (elements, running_pos)
294}
295
296fn snapshot_fallback(
297 inner: &TextDocumentInner,
298 frame_dto: &frontend::frame::dtos::FrameDto,
299) -> Vec<FlowElementSnapshot> {
300 let cell_frame_ids = build_cell_frame_ids(inner);
301
302 let block_ids = &frame_dto.blocks;
303 let mut block_dtos: Vec<_> = block_ids
304 .iter()
305 .filter_map(|&id| {
306 block_commands::get_block(&inner.ctx, &{ id })
307 .ok()
308 .flatten()
309 })
310 .collect();
311 block_dtos.sort_by_key(|b| b.document_position);
312
313 let mut elements: Vec<FlowElementSnapshot> = block_dtos
314 .iter()
315 .filter_map(|b| crate::text_block::build_block_snapshot(inner, b.id))
316 .map(FlowElementSnapshot::Block)
317 .collect();
318
319 let all_frames = frame_commands::get_all_frame(&inner.ctx).unwrap_or_default();
320 for f in &all_frames {
321 if f.id == frame_dto.id {
322 continue;
323 }
324 if cell_frame_ids.contains(&(f.id as EntityId)) {
325 continue;
326 }
327 if f.parent_frame == Some(frame_dto.id) {
328 if let Some(table_id) = f.table {
329 if let Some(snap) = build_table_snapshot(inner, table_id) {
330 elements.push(FlowElementSnapshot::Table(snap));
331 }
332 } else {
333 let nested = build_flow_snapshot(inner, f.id as EntityId);
334 elements.push(FlowElementSnapshot::Frame(FrameSnapshot {
335 frame_id: f.id as usize,
336 format: frame_dto_to_format(f),
337 elements: nested,
338 }));
339 }
340 }
341 }
342
343 elements
344}
345
346pub(crate) fn build_table_snapshot(
348 inner: &TextDocumentInner,
349 table_id: u64,
350) -> Option<TableSnapshot> {
351 let table_dto = table_commands::get_table(&inner.ctx, &table_id)
352 .ok()
353 .flatten()?;
354
355 let mut cells = Vec::new();
356 for &cell_id in &table_dto.cells {
357 if let Some(cell_dto) = table_cell_commands::get_table_cell(&inner.ctx, &{ cell_id })
358 .ok()
359 .flatten()
360 {
361 let blocks = if let Some(cell_frame_id) = cell_dto.cell_frame {
362 crate::text_block::build_blocks_snapshot_for_frame(inner, cell_frame_id)
363 } else {
364 Vec::new()
365 };
366 cells.push(CellSnapshot {
367 row: to_usize(cell_dto.row),
368 column: to_usize(cell_dto.column),
369 row_span: to_usize(cell_dto.row_span),
370 column_span: to_usize(cell_dto.column_span),
371 format: cell_dto_to_format(&cell_dto),
372 blocks,
373 });
374 }
375 }
376
377 Some(TableSnapshot {
378 table_id: table_id as usize,
379 rows: to_usize(table_dto.rows),
380 columns: to_usize(table_dto.columns),
381 column_widths: table_dto.column_widths.iter().map(|&v| v as i32).collect(),
382 format: table_dto_to_format(&table_dto),
383 cells,
384 })
385}
386
387pub(crate) fn frame_dto_to_format(f: &frontend::frame::dtos::FrameDto) -> FrameFormat {
392 FrameFormat {
393 height: f.fmt_height.map(|v| v as i32),
394 width: f.fmt_width.map(|v| v as i32),
395 top_margin: f.fmt_top_margin.map(|v| v as i32),
396 bottom_margin: f.fmt_bottom_margin.map(|v| v as i32),
397 left_margin: f.fmt_left_margin.map(|v| v as i32),
398 right_margin: f.fmt_right_margin.map(|v| v as i32),
399 padding: f.fmt_padding.map(|v| v as i32),
400 border: f.fmt_border.map(|v| v as i32),
401 position: f.fmt_position.clone(),
402 is_blockquote: f.fmt_is_blockquote,
403 }
404}
405
406pub(crate) fn table_dto_to_format(t: &frontend::table::dtos::TableDto) -> crate::flow::TableFormat {
407 crate::flow::TableFormat {
408 border: t.fmt_border.map(|v| v as i32),
409 cell_spacing: t.fmt_cell_spacing.map(|v| v as i32),
410 cell_padding: t.fmt_cell_padding.map(|v| v as i32),
411 width: t.fmt_width.map(|v| v as i32),
412 alignment: t.fmt_alignment.clone(),
413 }
414}
415
416pub(crate) fn cell_dto_to_format(
417 c: &frontend::table_cell::dtos::TableCellDto,
418) -> crate::flow::CellFormat {
419 use frontend::common::entities::CellVerticalAlignment as BackendCVA;
420 crate::flow::CellFormat {
421 padding: c.fmt_padding.map(|v| v as i32),
422 border: c.fmt_border.map(|v| v as i32),
423 vertical_alignment: c.fmt_vertical_alignment.as_ref().map(|v| match v {
424 BackendCVA::Top => crate::flow::CellVerticalAlignment::Top,
425 BackendCVA::Middle => crate::flow::CellVerticalAlignment::Middle,
426 BackendCVA::Bottom => crate::flow::CellVerticalAlignment::Bottom,
427 }),
428 background_color: c.fmt_background_color.clone(),
429 }
430}