Skip to main content

cursive_image/
image_view.rs

1use super::{image::*, sizing::*, state::*, vec2f::*};
2
3use {
4    crossterm::terminal::*,
5    cursive::{align::*, *},
6    std::{cell::*, sync::*},
7};
8
9//
10// ImageView
11//
12
13/// Image view.
14///
15/// Works on terminals that support the
16/// [Kitty graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol.html)
17///
18/// The view supports shrink, fit, and scale sizing modes (shrink is the default), image alignment,
19/// and scrolling.
20pub struct ImageView {
21    pub(crate) image: Option<Image>,
22    pub(crate) sizing: Sizing,
23    pub(crate) align: Align,
24
25    pub(crate) size: Option<Vec2>,
26    pub(crate) content_offset: Mutex<RefCell<Option<Vec2>>>,
27    pub(crate) state: Mutex<RefCell<State>>,
28
29    pub(crate) cell_size: XY<f64>,
30    pub(crate) cell_aspect_ratio: f64,
31}
32
33impl ImageView {
34    /// Image.
35    pub fn image(&self) -> Option<&Image> {
36        self.image.as_ref()
37    }
38
39    /// Set image.
40    pub fn set_image(&mut self, image: Image) {
41        // Note: the existing image will be dropped and thus deleted
42        self.image = Some(image);
43        self.reset_layout();
44    }
45
46    /// Take image.
47    ///
48    /// You muse manually [release](Image::release) or [hide](Image::hide) the image.
49    pub fn take_image(&mut self) -> Option<Image> {
50        self.reset_layout();
51        self.image.take()
52    }
53
54    /// Set image.
55    ///
56    /// Chainable.
57    pub fn with_image(self, image: Image) -> Self {
58        self.with(|self_| self_.set_image(image))
59    }
60
61    /// Sizing.
62    pub fn sizing(&self) -> Sizing {
63        self.sizing
64    }
65
66    /// Set sizing.
67    pub fn set_sizing(&mut self, sizing: Sizing) {
68        if self.sizing != sizing {
69            self.sizing = sizing;
70            self.reset_layout();
71        }
72    }
73
74    /// Set sizing.
75    ///
76    /// Chainable.
77    pub fn with_sizing(self, sizing: Sizing) -> Self {
78        self.with(|self_| self_.set_sizing(sizing))
79    }
80
81    /// Align.
82    pub fn align(&self) -> Align {
83        self.align
84    }
85
86    /// Set align.
87    pub fn set_align(&mut self, align: Align) {
88        if self.align != align {
89            self.align = align;
90            self.reset_layout();
91        }
92    }
93
94    /// Set align.
95    ///
96    /// Chainable.
97    pub fn with_align(self, align: Align) -> Self {
98        self.with(|self_| self_.set_align(align))
99    }
100
101    /// Hide the image.
102    pub fn hide(&self) {
103        if let Some(image) = &self.image {
104            _ = image.hide();
105        }
106    }
107
108    // Unset size and content_offset.
109    fn reset_layout(&mut self) {
110        self.size = None;
111        self.set_content_offset(None);
112    }
113
114    pub(crate) fn get_state(&self) -> State {
115        *self.state.lock().unwrap().borrow()
116    }
117
118    pub(crate) fn set_state(&self, state: State) {
119        *self.state.lock().unwrap().borrow_mut() = state;
120    }
121
122    pub(crate) fn get_content_offset(&self) -> Option<Vec2> {
123        *self.content_offset.lock().unwrap().borrow()
124    }
125
126    pub(crate) fn set_content_offset(&self, content_offset: Option<Vec2>) {
127        *self.content_offset.lock().unwrap().borrow_mut() = content_offset;
128    }
129}
130
131impl Default for ImageView {
132    fn default() -> Self {
133        let cell_size = cell_size();
134        Self {
135            image: None,
136            sizing: Default::default(),
137            align: Align::center(),
138            size: None,
139            content_offset: Default::default(),
140            state: Default::default(),
141            cell_size,
142            cell_aspect_ratio: cell_size.aspect_ratio(),
143        }
144    }
145}
146
147// Utils
148
149// Cell size in pixels.
150fn cell_size() -> Vec2f {
151    match window_size() {
152        Ok(size) if size.width != 0 && size.height != 0 && size.columns != 0 && size.rows != 0 => {
153            (size.width as f64 / size.columns as f64, size.height as f64 / size.rows as f64)
154        }
155
156        _ => (8., 8.),
157    }
158    .into()
159}