1use crate::config::v2::tui::{Alignment, CoverArtPosition};
2use anyhow::{Result, bail};
3use image::DynamicImage;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct AlignmentWrap(Alignment);
7
8impl AlignmentWrap {
9 const fn x(&self, absolute_x: u32, width: u32) -> u32 {
10 match self.0 {
11 Alignment::BottomRight | Alignment::TopRight => {
12 Self::get_size_substract(absolute_x, width)
13 }
14 Alignment::BottomLeft | Alignment::TopLeft => absolute_x,
15 }
16 }
17 const fn y(&self, absolute_y: u32, height: u32) -> u32 {
18 match self.0 {
19 Alignment::BottomRight | Alignment::BottomLeft => {
20 Self::get_size_substract(absolute_y, height / 2)
21 }
22 Alignment::TopRight | Alignment::TopLeft => absolute_y,
23 }
24 }
25
26 const fn get_size_substract(absolute_size: u32, size: u32) -> u32 {
27 if absolute_size > size {
28 return absolute_size - size;
29 }
30 0
31 }
32}
33
34#[derive(Clone, Debug)]
35pub struct Xywh {
36 pub x_between_1_100: u32,
37 pub y_between_1_100: u32,
38 pub width_between_1_100: u32,
39 pub x: u32,
40 pub y: u32,
41 pub width: u32,
42 pub height: u32,
43 pub align: AlignmentWrap,
44}
45
46impl From<&CoverArtPosition> for Xywh {
47 fn from(value: &CoverArtPosition) -> Self {
48 Self {
50 align: AlignmentWrap(value.align),
51 ..Default::default()
52 }
53 }
54}
55
56impl Default for Xywh {
57 #[allow(clippy::cast_lossless, clippy::cast_possible_truncation)]
58 fn default() -> Self {
59 let width = 20_u32;
60 let height = 20_u32;
61 let (term_width, term_height) = Self::get_terminal_size_u32();
62 let x = term_width.saturating_sub(1);
63 let y = term_height.saturating_sub(9);
64
65 Self {
66 x_between_1_100: 100,
67 y_between_1_100: 77,
68 width_between_1_100: width,
69 x,
70 y,
71 width,
72 height,
73 align: AlignmentWrap(Alignment::default()),
74 }
75 }
76}
77impl Xywh {
78 pub fn move_left(&mut self) {
79 self.x_between_1_100 = self.x_between_1_100.saturating_sub(1);
80 }
81
82 pub fn move_right(&mut self) {
83 self.x_between_1_100 += 1;
84 self.x_between_1_100 = self.x_between_1_100.min(100);
85 }
86
87 pub fn move_up(&mut self) {
88 self.y_between_1_100 = self.y_between_1_100.saturating_sub(2);
89 }
90
91 pub fn move_down(&mut self) {
92 self.y_between_1_100 += 2;
93 self.y_between_1_100 = self.y_between_1_100.min(100);
94 }
95 pub fn zoom_in(&mut self) {
96 self.width_between_1_100 += 1;
97 self.width_between_1_100 = self.width_between_1_100.min(100);
98 }
99
100 pub fn zoom_out(&mut self) {
101 self.width_between_1_100 = self.width_between_1_100.saturating_sub(1);
102 }
103
104 pub fn update_size(&self, image: &DynamicImage) -> Result<Self> {
105 let (term_width, term_height) = Self::get_terminal_size_u32();
106 let (x, y, width, height) = self.calculate_xywh(term_width, term_height, image)?;
107 Ok(Self {
108 x_between_1_100: self.x_between_1_100,
109 y_between_1_100: self.y_between_1_100,
110 width_between_1_100: self.width_between_1_100,
111 x,
112 y,
113 width,
114 height,
115 align: self.align.clone(),
116 })
117 }
118 fn calculate_xywh(
119 &self,
120 term_width: u32,
121 term_height: u32,
122 image: &DynamicImage,
123 ) -> Result<(u32, u32, u32, u32)> {
124 let width = self.get_width(term_width)?;
125 let height = Self::get_height(width, term_height, image)?;
126 let (absolute_x, absolute_y) = (
127 self.x_between_1_100 * term_width / 100,
128 self.y_between_1_100 * term_height / 100,
129 );
130 let (x, y) = (
131 self.align.x(absolute_x, width),
132 self.align.y(absolute_y, height),
133 );
134 Ok((x, y, width, height))
135 }
136
137 fn get_width(&self, term_width: u32) -> Result<u32> {
138 let width = self.width_between_1_100 * term_width / 100;
139 Self::safe_guard_width_or_height(width, term_width)
140 }
141
142 fn safe_guard_width_or_height(size: u32, size_max: u32) -> Result<u32> {
143 if size > size_max {
144 bail!("image width is too big, please reduce image width");
145 }
146 Ok(size)
147 }
148
149 fn get_height(width: u32, term_height: u32, image: &DynamicImage) -> Result<u32> {
150 let (pic_width_orig, pic_height_orig) = image::GenericImageView::dimensions(image);
151 let height = (width * pic_height_orig) / (pic_width_orig);
152 Self::safe_guard_width_or_height(height, term_height * 2)
153 }
154
155 #[must_use]
156 pub fn get_terminal_size_u32() -> (u32, u32) {
157 let (term_width, term_height) = viuer::terminal_size();
158 (u32::from(term_width), u32::from(term_height))
159 }
160}