matchmaker/ui/
preview.rs

1use log::{error};
2use ratatui::{
3    layout::Rect,
4    widgets::{Paragraph, Wrap},
5};
6
7use crate::{
8    config::{PreviewConfig, PreviewLayoutSetting},
9    proc::Preview,
10};
11
12#[derive(Debug)]
13pub struct PreviewUI {
14    pub view: Preview,
15    config: PreviewConfig,
16    pub layout_idx: usize,
17    pub area: Rect,
18    pub offset: u16,
19}
20
21impl PreviewUI {
22    pub fn new(view: Preview, config: PreviewConfig) -> Self {
23        Self {
24            view,
25            config,
26            layout_idx: 0,
27            offset: 0,
28            area: Rect::default()
29        }
30    }
31    
32    pub fn is_show(&self) -> bool {
33        self.config.show && self.layout().max != 0 // sentinel for hidden preview
34        // btw, for running background tasks, use an event handler
35    }
36    pub fn show<const SHOW: bool>(&mut self) -> bool {
37        let previous = self.config.show;
38        self.config.show = SHOW;
39        previous != SHOW
40    }
41    pub fn toggle_show(&mut self) {
42        self.config.show = !self.config.show;
43    }
44    
45    pub fn layout(&self) -> &PreviewLayoutSetting {
46        &self.config.layout[self.layout_idx].layout
47    }
48    pub fn command(&self) -> &String {
49        &self.config.layout[self.layout_idx].command
50    }
51    
52    pub fn wrap(&mut self, wrap: bool) {
53        self.config.wrap = wrap;
54    }
55    
56    pub fn is_wrap(&self) -> bool {
57        self.config.wrap
58    }
59    
60    // pub fn up(&mut self, n: u16) {
61    //     if self.offset >= n {
62    //         self.offset -= n;
63    //     } else {
64    //         self.offset = 0;
65    //     }
66    // }
67    // pub fn down(&mut self, n: u16) {
68    //     let total_lines = self.view.len() as u16;
69    //     self.offset = (total_lines + self.area.height).min(self.offset + n);
70    // }
71    
72    pub fn up(&mut self, n: u16) {
73        if self.offset >= n {
74            self.offset -= n;
75        } else if self.config.scroll_wrap {
76            let total_lines = self.view.len() as u16;
77            self.offset = total_lines.saturating_sub(n - self.offset);
78        } else {
79            self.offset = 0;
80        }
81    }
82    
83    pub fn down(&mut self, n: u16) {
84        let total_lines = self.view.len() as u16;
85        
86        if self.offset + n > total_lines {
87            if self.config.scroll_wrap {
88                self.offset = 0;
89            } else {
90                self.offset = total_lines;
91            }
92        } else {
93            self.offset += n;
94        }
95    }
96    
97    
98    pub fn update_dimensions(&mut self, area: &Rect) {
99        let mut height = area.height;
100        height -= self.config.border.height();
101        self.area.height = height;
102        
103        let mut width = area.width;
104        width -= self.config.border.width();
105        self.area.width = width;
106    }
107    
108    pub fn cycle_layout(&mut self) {
109        self.layout_idx = (self.layout_idx + 1) % self.config.layout.len()
110    }
111    
112    
113    pub fn set_idx(&mut self, idx: u8) -> bool {
114        let idx = idx as usize;
115        if idx <= self.config.layout.len() {
116            let changed = self.layout_idx != idx;
117            self.layout_idx = idx;
118            changed
119        } else {
120            error!("Layout idx {idx} out of bounds, ignoring.");
121            false
122        }
123    }
124    
125    pub fn make_preview(&self) -> Paragraph<'_> {
126        let results = self.view.results();
127        let height = self.area.height as usize;
128        let offset = self.offset as usize;
129        
130        // todo: can we avoid cloning?
131        let visible_lines: Vec<_> = results
132        .iter()
133        .skip(offset)
134        .take(height)
135        .cloned()
136        .collect();
137        
138        let mut preview = Paragraph::new(visible_lines);
139        preview = preview.block(self.config.border.as_block());
140        if self.config.wrap {
141            preview = preview.wrap(Wrap { trim: true });
142        }
143        preview
144    }
145}