Skip to content

Commit 9dfd15d

Browse files
committed
mouse select support
1 parent a6773bc commit 9dfd15d

File tree

11 files changed

+196
-113
lines changed

11 files changed

+196
-113
lines changed

src/app/jwt_decoder.rs

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use serde_derive::{Deserialize, Serialize};
1111
use serde_json::{to_string_pretty, Value};
1212

1313
use super::{
14-
models::{ScrollableTxt, TabRoute, TabsState},
14+
models::{BlockState, ScrollableTxt},
1515
utils::{
1616
decoding_key_from_jwks_secret, get_secret_from_file_or_input, JWTError, JWTResult, SecretType,
1717
},
@@ -25,7 +25,7 @@ pub struct Decoder {
2525
pub payload: ScrollableTxt,
2626
pub secret: TextInput,
2727
pub signature_verified: bool,
28-
pub blocks: TabsState,
28+
pub blocks: BlockState,
2929
pub utc_dates: bool,
3030
pub ignore_exp: bool,
3131
/// do not manipulate directly, use `set_decoded` instead
@@ -38,34 +38,22 @@ impl Decoder {
3838
encoded: TextInput::new(token.unwrap_or_default()),
3939
secret: TextInput::new(secret),
4040
ignore_exp: true,
41-
blocks: TabsState::new(vec![
42-
TabRoute {
43-
title: "".into(),
44-
route: Route {
45-
id: RouteId::Decoder,
46-
active_block: ActiveBlock::DecoderToken,
47-
},
41+
blocks: BlockState::new(vec![
42+
Route {
43+
id: RouteId::Decoder,
44+
active_block: ActiveBlock::DecoderToken,
4845
},
49-
TabRoute {
50-
title: "".into(),
51-
route: Route {
52-
id: RouteId::Decoder,
53-
active_block: ActiveBlock::DecoderSecret,
54-
},
46+
Route {
47+
id: RouteId::Decoder,
48+
active_block: ActiveBlock::DecoderSecret,
5549
},
56-
TabRoute {
57-
title: "".into(),
58-
route: Route {
59-
id: RouteId::Decoder,
60-
active_block: ActiveBlock::DecoderHeader,
61-
},
50+
Route {
51+
id: RouteId::Decoder,
52+
active_block: ActiveBlock::DecoderHeader,
6253
},
63-
TabRoute {
64-
title: "".into(),
65-
route: Route {
66-
id: RouteId::Decoder,
67-
active_block: ActiveBlock::DecoderPayload,
68-
},
54+
Route {
55+
id: RouteId::Decoder,
56+
active_block: ActiveBlock::DecoderPayload,
6957
},
7058
]),
7159
..Decoder::default()

src/app/jwt_encoder.rs

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use jsonwebtoken::{errors::Error, Algorithm, EncodingKey, Header};
22

33
use super::{
44
jwt_decoder::Payload,
5-
models::{ScrollableTxt, TabRoute, TabsState},
5+
models::{BlockState, ScrollableTxt},
66
utils::{get_secret_from_file_or_input, JWTError, JWTResult, SecretType},
77
ActiveBlock, App, Route, RouteId, TextAreaInput, TextInput,
88
};
@@ -14,7 +14,7 @@ pub struct Encoder<'a> {
1414
pub payload: TextAreaInput<'a>,
1515
pub secret: TextInput,
1616
pub signature_verified: bool,
17-
pub blocks: TabsState,
17+
pub blocks: BlockState,
1818
}
1919

2020
impl Encoder<'_> {
@@ -29,34 +29,22 @@ impl Encoder<'_> {
2929
Self {
3030
header,
3131
secret: TextInput::new(secret),
32-
blocks: TabsState::new(vec![
33-
TabRoute {
34-
title: "".into(),
35-
route: Route {
36-
id: RouteId::Encoder,
37-
active_block: ActiveBlock::EncoderHeader,
38-
},
32+
blocks: BlockState::new(vec![
33+
Route {
34+
id: RouteId::Encoder,
35+
active_block: ActiveBlock::EncoderHeader,
3936
},
40-
TabRoute {
41-
title: "".into(),
42-
route: Route {
43-
id: RouteId::Encoder,
44-
active_block: ActiveBlock::EncoderPayload,
45-
},
37+
Route {
38+
id: RouteId::Encoder,
39+
active_block: ActiveBlock::EncoderPayload,
4640
},
47-
TabRoute {
48-
title: "".into(),
49-
route: Route {
50-
id: RouteId::Encoder,
51-
active_block: ActiveBlock::EncoderSecret,
52-
},
41+
Route {
42+
id: RouteId::Encoder,
43+
active_block: ActiveBlock::EncoderSecret,
5344
},
54-
TabRoute {
55-
title: "".into(),
56-
route: Route {
57-
id: RouteId::Encoder,
58-
active_block: ActiveBlock::EncoderToken,
59-
},
45+
Route {
46+
id: RouteId::Encoder,
47+
active_block: ActiveBlock::EncoderToken,
6048
},
6149
]),
6250
..Encoder::default()

src/app/mod.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ pub(crate) mod key_binding;
44
pub(crate) mod models;
55
pub(crate) mod utils;
66

7+
use std::collections::HashMap;
8+
79
use ratatui::layout::Rect;
810
use tui_input::Input;
911
use tui_textarea::TextArea;
@@ -16,7 +18,7 @@ use self::{
1618
utils::JWTError,
1719
};
1820

19-
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
21+
#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
2022
pub enum ActiveBlock {
2123
Help,
2224
DecoderToken,
@@ -29,14 +31,14 @@ pub enum ActiveBlock {
2931
EncoderSecret,
3032
}
3133

32-
#[derive(Clone, Eq, PartialEq, Debug)]
34+
#[derive(Clone, Copy, Eq, Hash, PartialEq, Debug)]
3335
pub enum RouteId {
3436
Help,
3537
Decoder,
3638
Encoder,
3739
}
3840

39-
#[derive(Debug, Clone)]
41+
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
4042
pub struct Route {
4143
pub id: RouteId,
4244
pub active_block: ActiveBlock,
@@ -110,6 +112,7 @@ pub struct App {
110112
pub confirm: bool,
111113
pub light_theme: bool,
112114
pub help_docs: StatefulTable<Vec<String>>,
115+
pub block_map: HashMap<Route, Rect>,
113116
pub data: Data,
114117
}
115118

@@ -143,6 +146,7 @@ impl Default for App {
143146
confirm: false,
144147
light_theme: false,
145148
help_docs: StatefulTable::with_items(key_binding::get_help_docs()),
149+
block_map: HashMap::new(),
146150
data: Data::default(),
147151
}
148152
}
@@ -161,6 +165,14 @@ impl App {
161165
}
162166
}
163167

168+
pub fn update_block_map(&mut self, block: Route, area: Rect) {
169+
self
170+
.block_map
171+
.entry(block)
172+
.and_modify(|w| *w = area)
173+
.or_insert(area);
174+
}
175+
164176
pub fn refresh(&mut self) {
165177
self.data.error = String::new();
166178
self.data = Data::default();
@@ -197,17 +209,17 @@ impl App {
197209

198210
pub fn cycle_main_routes(&mut self) {
199211
self.main_tabs.next();
200-
let route = self.main_tabs.get_active_route().clone();
201-
self.push_navigation_route(route);
212+
let route = self.main_tabs.get_active_route();
213+
self.push_navigation_route(*route);
202214
}
203215

204216
pub fn route_decoder(&mut self) {
205-
let route = self.main_tabs.set_index(0).route.clone();
217+
let route = self.main_tabs.set_index(0).route;
206218
self.push_navigation_route(route);
207219
}
208220

209221
pub fn route_encoder(&mut self) {
210-
let route = self.main_tabs.set_index(1).route.clone();
222+
let route = self.main_tabs.set_index(1).route;
211223
self.push_navigation_route(route);
212224
}
213225

src/app/models.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,45 @@ impl TabsState {
103103
pub fn get_active_route(&self) -> &Route {
104104
&self.items[self.index].route
105105
}
106+
pub fn next(&mut self) {
107+
self.index = (self.index + 1) % self.items.len();
108+
}
109+
#[allow(dead_code)]
110+
pub fn previous(&mut self) {
111+
if self.index > 0 {
112+
self.index -= 1;
113+
} else {
114+
self.index = self.items.len() - 1;
115+
}
116+
}
117+
}
106118

119+
#[derive(Default)]
120+
pub struct BlockState {
121+
pub items: Vec<Route>,
122+
pub index: usize,
123+
}
124+
125+
impl BlockState {
126+
pub fn new(items: Vec<Route>) -> BlockState {
127+
BlockState { items, index: 0 }
128+
}
129+
pub fn set_item(&mut self, block: Route) -> &Route {
130+
// find active block index
131+
let index = self
132+
.items
133+
.iter()
134+
.position(|it| *it == block)
135+
.unwrap_or_default();
136+
self.index = index;
137+
&self.items[self.index]
138+
}
139+
pub fn get_active_item(&self) -> &Route {
140+
&self.items[self.index]
141+
}
142+
pub fn get_active_block(&self) -> &ActiveBlock {
143+
&self.items[self.index].active_block
144+
}
107145
pub fn next(&mut self) {
108146
self.index = (self.index + 1) % self.items.len();
109147
}

src/event/events.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ impl Events {
6767
.unwrap_or_else(|| Duration::from_secs(0));
6868
// poll for tick rate duration, if no event, sent tick event.
6969
if event::poll(timeout).unwrap() {
70-
match event::read().unwrap() {
70+
let e = event::read().unwrap();
71+
match e {
7172
CEvent::Key(key_event) => handle_key_event(&event_tx, key_event),
7273
CEvent::Mouse(mouse_event) => {
7374
event_tx.send(Event::MouseInput(mouse_event)).unwrap();

src/handlers/mod.rs

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use crossterm::event::{Event, KeyEvent, MouseEvent, MouseEventKind};
1+
use crossterm::event::{Event, KeyEvent, MouseButton, MouseEvent, MouseEventKind};
2+
use ratatui::layout::Rect;
23
use tui_input::{backend::crossterm::EventHandler, Input};
34
use tui_textarea::TextArea;
45

@@ -78,6 +79,7 @@ pub fn handle_mouse_events(mouse: MouseEvent, app: &mut App) {
7879
// mouse scrolling is inverted
7980
MouseEventKind::ScrollDown => handle_block_scroll(app, true, true, false),
8081
MouseEventKind::ScrollUp => handle_block_scroll(app, false, true, false),
82+
MouseEventKind::Down(MouseButton::Left) => handle_mouse_btn_press(app, mouse),
8183
_ => { /* do nothing */ }
8284
}
8385
}
@@ -185,12 +187,11 @@ fn handle_route_events(key: Key, app: &mut App) {
185187
_ if key == DEFAULT_KEYBINDING.toggle_ignore_exp.key => {
186188
app.data.decoder.ignore_exp = !app.data.decoder.ignore_exp;
187189
}
188-
// as these are tabs with index the order here matters, at least for readability
189-
_ => {}
190+
_ => { /* Do nothing */ }
190191
};
191192
}
192193
RouteId::Encoder => {
193-
// todo!()
194+
// nothing to handle
194195
}
195196
_ => { /* Do nothing */ }
196197
}
@@ -201,11 +202,11 @@ fn handle_left_key_events(app: &mut App) {
201202
match app.get_current_route().id {
202203
RouteId::Decoder => {
203204
app.data.decoder.blocks.previous();
204-
app.push_navigation_route(app.data.decoder.blocks.get_active_route().clone());
205+
app.push_navigation_route(*app.data.decoder.blocks.get_active_item());
205206
}
206207
RouteId::Encoder => {
207208
app.data.encoder.blocks.previous();
208-
app.push_navigation_route(app.data.encoder.blocks.get_active_route().clone());
209+
app.push_navigation_route(*app.data.encoder.blocks.get_active_item());
209210
}
210211
RouteId::Help => { /* Do nothing */ }
211212
}
@@ -216,16 +217,46 @@ fn handle_right_key_events(app: &mut App) {
216217
match app.get_current_route().id {
217218
RouteId::Decoder => {
218219
app.data.decoder.blocks.next();
219-
app.push_navigation_route(app.data.decoder.blocks.get_active_route().clone());
220+
app.push_navigation_route(*app.data.decoder.blocks.get_active_item());
220221
}
221222
RouteId::Encoder => {
222223
app.data.encoder.blocks.next();
223-
app.push_navigation_route(app.data.encoder.blocks.get_active_route().clone());
224+
app.push_navigation_route(*app.data.encoder.blocks.get_active_item());
224225
}
225226
RouteId::Help => { /* Do nothing */ }
226227
}
227228
}
228229

230+
fn handle_mouse_btn_press(app: &mut App, mouse_event: MouseEvent) {
231+
if let Some(data) = app
232+
.block_map
233+
.iter()
234+
.filter(|i| {
235+
i.0.id == app.get_current_route().id
236+
&& i
237+
.1
238+
.intersects(Rect::new(mouse_event.column, mouse_event.row, 1, 1))
239+
})
240+
.collect::<Vec<_>>()
241+
.first()
242+
{
243+
let selected_route = *data.0;
244+
245+
// route specific events
246+
match app.get_current_route().id {
247+
RouteId::Decoder => {
248+
app.data.decoder.blocks.set_item(selected_route);
249+
app.push_navigation_route(*app.data.decoder.blocks.get_active_item());
250+
}
251+
RouteId::Encoder => {
252+
app.data.encoder.blocks.set_item(selected_route);
253+
app.push_navigation_route(*app.data.encoder.blocks.get_active_item());
254+
}
255+
RouteId::Help => { /* Do nothing */ }
256+
}
257+
};
258+
}
259+
229260
fn handle_block_scroll(app: &mut App, up: bool, is_mouse: bool, page: bool) {
230261
match app.get_current_route().active_block {
231262
ActiveBlock::Help => app.help_docs.handle_scroll(up, page),
@@ -330,7 +361,7 @@ mod tests {
330361

331362
app.data.encoder.header.input_mode = InputMode::Editing;
332363

333-
let route = app.main_tabs.set_index(1).route.clone();
364+
let route = app.main_tabs.set_index(1).route;
334365
app.push_navigation_route(route);
335366

336367
assert_eq!(app.data.encoder.header.input_mode, InputMode::Editing);

0 commit comments

Comments
 (0)