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
53pub(crate) fn build_flow_elements(
62 inner: &TextDocumentInner,
63 doc_arc: &Arc<Mutex<TextDocumentInner>>,
64 frame_id: EntityId,
65) -> Vec<FlowElement> {
66 let frame_dto = match frame_commands::get_frame(&inner.ctx, &frame_id)
67 .ok()
68 .flatten()
69 {
70 Some(f) => f,
71 None => return Vec::new(),
72 };
73
74 if !frame_dto.child_order.is_empty() {
75 flow_from_child_order(inner, doc_arc, &frame_dto.child_order)
76 } else {
77 flow_fallback(inner, doc_arc, &frame_dto)
78 }
79}
80
81fn flow_from_child_order(
83 inner: &TextDocumentInner,
84 doc_arc: &Arc<Mutex<TextDocumentInner>>,
85 child_order: &[i64],
86) -> Vec<FlowElement> {
87 let mut elements = Vec::with_capacity(child_order.len());
88
89 for &entry in child_order {
90 if entry > 0 {
91 elements.push(FlowElement::Block(TextBlock {
93 doc: Arc::clone(doc_arc),
94 block_id: entry as usize,
95 }));
96 } else if entry < 0 {
97 let sub_frame_id = (-entry) as EntityId;
99 if let Some(sub_frame) = frame_commands::get_frame(&inner.ctx, &sub_frame_id)
100 .ok()
101 .flatten()
102 {
103 if let Some(table_id) = sub_frame.table {
104 elements.push(FlowElement::Table(TextTable {
106 doc: Arc::clone(doc_arc),
107 table_id: table_id as usize,
108 }));
109 } else {
110 elements.push(FlowElement::Frame(TextFrame {
112 doc: Arc::clone(doc_arc),
113 frame_id: sub_frame_id as usize,
114 }));
115 }
116 }
117 }
118 }
120
121 elements
122}
123
124fn flow_fallback(
126 inner: &TextDocumentInner,
127 doc_arc: &Arc<Mutex<TextDocumentInner>>,
128 frame_dto: &frontend::frame::dtos::FrameDto,
129) -> Vec<FlowElement> {
130 let cell_frame_ids = build_cell_frame_ids(inner);
132
133 let block_ids = &frame_dto.blocks;
135 let mut block_dtos: Vec<_> = block_ids
136 .iter()
137 .filter_map(|&id| {
138 block_commands::get_block(&inner.ctx, &{ id })
139 .ok()
140 .flatten()
141 })
142 .collect();
143 block_dtos.sort_by_key(|b| b.document_position);
144
145 let mut elements: Vec<FlowElement> = block_dtos
146 .iter()
147 .map(|b| {
148 FlowElement::Block(TextBlock {
149 doc: Arc::clone(doc_arc),
150 block_id: b.id as usize,
151 })
152 })
153 .collect();
154
155 let all_frames = frame_commands::get_all_frame(&inner.ctx).unwrap_or_default();
160 for f in &all_frames {
161 if f.id == frame_dto.id {
162 continue; }
164 if cell_frame_ids.contains(&(f.id as EntityId)) {
165 continue; }
167 if f.parent_frame == Some(frame_dto.id) {
169 if let Some(table_id) = f.table {
170 elements.push(FlowElement::Table(TextTable {
171 doc: Arc::clone(doc_arc),
172 table_id: table_id as usize,
173 }));
174 } else {
175 elements.push(FlowElement::Frame(TextFrame {
176 doc: Arc::clone(doc_arc),
177 frame_id: f.id as usize,
178 }));
179 }
180 }
181 }
182
183 elements
184}
185
186fn build_cell_frame_ids(inner: &TextDocumentInner) -> HashSet<EntityId> {
188 let mut ids = HashSet::new();
189 let all_cells = table_cell_commands::get_all_table_cell(&inner.ctx).unwrap_or_default();
190 for cell in &all_cells {
191 if let Some(frame_id) = cell.cell_frame {
192 ids.insert(frame_id);
193 }
194 }
195 ids
196}
197
198pub(crate) fn build_flow_snapshot(
204 inner: &TextDocumentInner,
205 frame_id: EntityId,
206) -> Vec<FlowElementSnapshot> {
207 let frame_dto = match frame_commands::get_frame(&inner.ctx, &frame_id)
208 .ok()
209 .flatten()
210 {
211 Some(f) => f,
212 None => return Vec::new(),
213 };
214
215 if !frame_dto.child_order.is_empty() {
216 snapshot_from_child_order(inner, &frame_dto.child_order)
217 } else {
218 snapshot_fallback(inner, &frame_dto)
219 }
220}
221
222fn snapshot_from_child_order(
223 inner: &TextDocumentInner,
224 child_order: &[i64],
225) -> Vec<FlowElementSnapshot> {
226 let mut elements = Vec::with_capacity(child_order.len());
227
228 for &entry in child_order {
229 if entry > 0 {
230 let block_id = entry as u64;
231 if let Some(snap) = crate::text_block::build_block_snapshot(inner, block_id) {
232 elements.push(FlowElementSnapshot::Block(snap));
233 }
234 } else if entry < 0 {
235 let sub_frame_id = (-entry) as EntityId;
236 if let Some(sub_frame) = frame_commands::get_frame(&inner.ctx, &sub_frame_id)
237 .ok()
238 .flatten()
239 {
240 if let Some(table_id) = sub_frame.table {
241 if let Some(snap) = build_table_snapshot(inner, table_id) {
242 elements.push(FlowElementSnapshot::Table(snap));
243 }
244 } else {
245 let nested = build_flow_snapshot(inner, sub_frame_id);
246 elements.push(FlowElementSnapshot::Frame(FrameSnapshot {
247 frame_id: sub_frame_id as usize,
248 format: frame_dto_to_format(&sub_frame),
249 elements: nested,
250 }));
251 }
252 }
253 }
254 }
255
256 elements
257}
258
259fn snapshot_fallback(
260 inner: &TextDocumentInner,
261 frame_dto: &frontend::frame::dtos::FrameDto,
262) -> Vec<FlowElementSnapshot> {
263 let cell_frame_ids = build_cell_frame_ids(inner);
264
265 let block_ids = &frame_dto.blocks;
266 let mut block_dtos: Vec<_> = block_ids
267 .iter()
268 .filter_map(|&id| {
269 block_commands::get_block(&inner.ctx, &{ id })
270 .ok()
271 .flatten()
272 })
273 .collect();
274 block_dtos.sort_by_key(|b| b.document_position);
275
276 let mut elements: Vec<FlowElementSnapshot> = block_dtos
277 .iter()
278 .filter_map(|b| crate::text_block::build_block_snapshot(inner, b.id))
279 .map(FlowElementSnapshot::Block)
280 .collect();
281
282 let all_frames = frame_commands::get_all_frame(&inner.ctx).unwrap_or_default();
283 for f in &all_frames {
284 if f.id == frame_dto.id {
285 continue;
286 }
287 if cell_frame_ids.contains(&(f.id as EntityId)) {
288 continue;
289 }
290 if f.parent_frame == Some(frame_dto.id) {
291 if let Some(table_id) = f.table {
292 if let Some(snap) = build_table_snapshot(inner, table_id) {
293 elements.push(FlowElementSnapshot::Table(snap));
294 }
295 } else {
296 let nested = build_flow_snapshot(inner, f.id as EntityId);
297 elements.push(FlowElementSnapshot::Frame(FrameSnapshot {
298 frame_id: f.id as usize,
299 format: frame_dto_to_format(f),
300 elements: nested,
301 }));
302 }
303 }
304 }
305
306 elements
307}
308
309pub(crate) fn build_table_snapshot(
311 inner: &TextDocumentInner,
312 table_id: u64,
313) -> Option<TableSnapshot> {
314 let table_dto = table_commands::get_table(&inner.ctx, &table_id)
315 .ok()
316 .flatten()?;
317
318 let mut cells = Vec::new();
319 for &cell_id in &table_dto.cells {
320 if let Some(cell_dto) = table_cell_commands::get_table_cell(&inner.ctx, &{ cell_id })
321 .ok()
322 .flatten()
323 {
324 let blocks = if let Some(cell_frame_id) = cell_dto.cell_frame {
325 crate::text_block::build_blocks_snapshot_for_frame(inner, cell_frame_id)
326 } else {
327 Vec::new()
328 };
329 cells.push(CellSnapshot {
330 row: to_usize(cell_dto.row),
331 column: to_usize(cell_dto.column),
332 row_span: to_usize(cell_dto.row_span),
333 column_span: to_usize(cell_dto.column_span),
334 format: cell_dto_to_format(&cell_dto),
335 blocks,
336 });
337 }
338 }
339
340 Some(TableSnapshot {
341 table_id: table_id as usize,
342 rows: to_usize(table_dto.rows),
343 columns: to_usize(table_dto.columns),
344 column_widths: table_dto.column_widths.iter().map(|&v| v as i32).collect(),
345 format: table_dto_to_format(&table_dto),
346 cells,
347 })
348}
349
350pub(crate) fn frame_dto_to_format(f: &frontend::frame::dtos::FrameDto) -> FrameFormat {
355 FrameFormat {
356 height: f.fmt_height.map(|v| v as i32),
357 width: f.fmt_width.map(|v| v as i32),
358 top_margin: f.fmt_top_margin.map(|v| v as i32),
359 bottom_margin: f.fmt_bottom_margin.map(|v| v as i32),
360 left_margin: f.fmt_left_margin.map(|v| v as i32),
361 right_margin: f.fmt_right_margin.map(|v| v as i32),
362 padding: f.fmt_padding.map(|v| v as i32),
363 border: f.fmt_border.map(|v| v as i32),
364 position: f.fmt_position.clone(),
365 }
366}
367
368pub(crate) fn table_dto_to_format(t: &frontend::table::dtos::TableDto) -> crate::flow::TableFormat {
369 crate::flow::TableFormat {
370 border: t.fmt_border.map(|v| v as i32),
371 cell_spacing: t.fmt_cell_spacing.map(|v| v as i32),
372 cell_padding: t.fmt_cell_padding.map(|v| v as i32),
373 width: t.fmt_width.map(|v| v as i32),
374 alignment: t.fmt_alignment.clone(),
375 }
376}
377
378pub(crate) fn cell_dto_to_format(
379 c: &frontend::table_cell::dtos::TableCellDto,
380) -> crate::flow::CellFormat {
381 use frontend::common::entities::CellVerticalAlignment as BackendCVA;
382 crate::flow::CellFormat {
383 padding: c.fmt_padding.map(|v| v as i32),
384 border: c.fmt_border.map(|v| v as i32),
385 vertical_alignment: c.fmt_vertical_alignment.as_ref().map(|v| match v {
386 BackendCVA::Top => crate::flow::CellVerticalAlignment::Top,
387 BackendCVA::Middle => crate::flow::CellVerticalAlignment::Middle,
388 BackendCVA::Bottom => crate::flow::CellVerticalAlignment::Bottom,
389 }),
390 background_color: c.fmt_background_color.clone(),
391 }
392}