sql_cli/widgets/
crosshair_widget.rs1use ratatui::{
9 layout::Rect,
10 style::{Color, Modifier, Style},
11 Frame,
12};
13use tracing::{debug, trace};
14
15#[derive(Debug, Clone, Default)]
17pub struct CrosshairPosition {
18 pub row: usize,
20 pub column: usize,
22 pub visible: bool,
24}
25
26pub struct CrosshairWidget {
28 position: CrosshairPosition,
30 style: Style,
32}
33
34impl Default for CrosshairWidget {
35 fn default() -> Self {
36 Self::new()
37 }
38}
39
40impl CrosshairWidget {
41 #[must_use]
43 pub fn new() -> Self {
44 Self {
45 position: CrosshairPosition::default(),
46 style: Style::default()
47 .bg(Color::DarkGray)
48 .add_modifier(Modifier::REVERSED),
49 }
50 }
51
52 pub fn set_position(&mut self, row: usize, column: usize) {
54 self.position.row = row;
55 self.position.column = column;
56 self.position.visible = true;
57 debug!("Crosshair position updated to ({}, {})", row, column);
58 }
59
60 pub fn hide(&mut self) {
62 self.position.visible = false;
63 }
64
65 pub fn show(&mut self) {
67 self.position.visible = true;
68 }
69
70 #[must_use]
72 pub fn position(&self) -> &CrosshairPosition {
73 &self.position
74 }
75
76 pub fn set_style(&mut self, style: Style) {
78 self.style = style;
79 }
80
81 pub fn render_overlay(
84 &self,
85 _f: &mut Frame,
86 table_area: Rect,
87 viewport_row_offset: usize,
88 viewport_col_offset: usize,
89 row_heights: &[u16], col_widths: &[u16], ) {
92 if !self.position.visible {
93 return;
94 }
95
96 let visible_rows = row_heights.len();
98 let visible_cols = col_widths.len();
99
100 if self.position.row < viewport_row_offset {
102 return; }
104 let relative_row = self.position.row - viewport_row_offset;
105 if relative_row >= visible_rows {
106 return; }
108
109 if self.position.column < viewport_col_offset {
110 return; }
112 let relative_col = self.position.column - viewport_col_offset;
113 if relative_col >= visible_cols {
114 return; }
116
117 let mut y = table_area.y + 2; for i in 0..relative_row {
120 y += row_heights[i];
121 }
122
123 let mut x = table_area.x + 1; for i in 0..relative_col {
125 x += col_widths[i] + 1; }
127
128 let cell_width = col_widths[relative_col];
130 let _cell_rect = Rect {
131 x,
132 y,
133 width: cell_width,
134 height: 1,
135 };
136
137 trace!(
141 "Rendering crosshair at viewport ({}, {}) -> screen ({}, {})",
142 relative_row,
143 relative_col,
144 x,
145 y
146 );
147 }
148
149 #[must_use]
151 pub fn calculate_scroll_offset(
152 &self,
153 current_row_offset: usize,
154 current_col_offset: usize,
155 viewport_height: usize,
156 viewport_width: usize,
157 ) -> (usize, usize) {
158 let mut new_row_offset = current_row_offset;
159 let mut new_col_offset = current_col_offset;
160
161 if self.position.row < current_row_offset {
163 new_row_offset = self.position.row;
165 } else if self.position.row >= current_row_offset + viewport_height {
166 new_row_offset = self.position.row.saturating_sub(viewport_height / 2);
168 }
169
170 if self.position.column < current_col_offset {
172 new_col_offset = self.position.column;
174 } else if self.position.column >= current_col_offset + viewport_width {
175 new_col_offset = self.position.column.saturating_sub(viewport_width / 2);
177 }
178
179 (new_row_offset, new_col_offset)
180 }
181}