diff --git a/src/view/app.rs b/src/view/app.rs index c50ba4fc..49390120 100644 --- a/src/view/app.rs +++ b/src/view/app.rs @@ -1,10 +1,10 @@ use std::sync::Arc; use ::crossterm::event::KeyCode; -use crossterm::event::{KeyEvent, KeyModifiers}; +use crossterm::event::{KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind}; use ratatui::Frame; use ratatui::buffer::Buffer; -use ratatui::layout::{Constraint, Layout, Rect}; +use ratatui::layout::{Constraint, Layout, Position, Rect}; use ratatui::widgets::{Block, Borders, Tabs, Widget}; use ratatui_image::picker::Picker; use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel}; @@ -62,6 +62,7 @@ where // terminal font size and the graphics it supports // if the terminal doesn't support any graphics protocol the picker is `None` picker: Option, + top_tabs_area: Rect, } impl Component for App @@ -79,6 +80,7 @@ where let [top_tabs_area, page_area] = main_layout.areas(area); + self.top_tabs_area = top_tabs_area; self.render_top_tabs(top_tabs_area, frame.buffer_mut()); self.render_pages(page_area, frame); @@ -92,6 +94,7 @@ where fn handle_events(&mut self, events: Events) { match events { Events::Key(key_event) => self.handle_key_events(key_event), + Events::Mouse(mouse_event) => self.handle_mouse_events(mouse_event), Events::GoToMangaPage(manga) => self.go_to_manga_page(manga), Events::ReadChapter(chapter_response, manga_to_read) => { self.go_to_read_chapter(chapter_response, manga_to_read, self.manga_tracker.clone()) @@ -176,6 +179,7 @@ where state: AppState::Runnning, error_message: None, manga_provider: Arc::clone(&provider), + top_tabs_area: Rect::default(), } } @@ -301,6 +305,30 @@ where } } + fn handle_mouse_events(&mut self, mouse_event: MouseEvent) { + if self.current_tab == SelectedPage::ReaderTab { + return; + } + if let MouseEventKind::Down(MouseButton::Left) = mouse_event.kind { + let pos = Position::new(mouse_event.column, mouse_event.row); + if !self.top_tabs_area.contains(pos) { + return; + } + let num_tabs: u16 = if self.current_tab == SelectedPage::MangaTab { 4 } else { 3 }; + let tab_width = self.top_tabs_area.width / num_tabs; + if tab_width == 0 { + return; + } + let tab_index = ((mouse_event.column.saturating_sub(self.top_tabs_area.x)) / tab_width).min(num_tabs - 1); + match tab_index { + 0 => self.global_event_tx.send(Events::GoToHome).ok(), + 1 => self.global_event_tx.send(Events::GoSearchPage).ok(), + 2 => self.global_event_tx.send(Events::GoFeedPage).ok(), + _ => None, + }; + } + } + fn go_search_page(&mut self) { if self.manga_page.is_some() { self.manga_page.as_mut().unwrap().clean_up(); diff --git a/src/view/pages/home.rs b/src/view/pages/home.rs index 4c92e18b..e6d652cb 100644 --- a/src/view/pages/home.rs +++ b/src/view/pages/home.rs @@ -1,11 +1,11 @@ use std::sync::Arc; use std::time::Duration; -use crossterm::event::{KeyCode, KeyEvent}; +use crossterm::event::{KeyCode, KeyEvent, MouseButton, MouseEvent, MouseEventKind}; use image::DynamicImage; use ratatui::Frame; use ratatui::buffer::Buffer; -use ratatui::layout::{Constraint, Layout, Margin, Rect}; +use ratatui::layout::{Constraint, Layout, Margin, Position, Rect}; use ratatui::style::Stylize; use ratatui::text::{Line, Span, ToSpan}; use ratatui::widgets::{Block, List, StatefulWidget, Widget}; @@ -67,6 +67,8 @@ where picker: Option, tasks: JoinSet<()>, manga_provider: Arc, + popular_mangas_area: Rect, + recently_added_area: Rect, } impl Component for Home @@ -81,6 +83,9 @@ where let [carrousel_popular_mangas_area, latest_updates_area] = layout.areas(area); + self.popular_mangas_area = carrousel_popular_mangas_area; + self.recently_added_area = latest_updates_area; + self.render_popular_mangas_carrousel(carrousel_popular_mangas_area, buf); self.render_recently_added_mangas_area(latest_updates_area, buf); @@ -115,6 +120,7 @@ where fn handle_events(&mut self, events: Events) { match events { Events::Key(key_event) => self.handle_key_events(key_event), + Events::Mouse(mouse_event) => self.handle_mouse_events(mouse_event), Events::Tick => self.tick(), _ => {}, } @@ -143,6 +149,8 @@ where recently_added_manga_state: ImageState::default(), tasks: JoinSet::new(), manga_provider, + popular_mangas_area: Rect::default(), + recently_added_area: Rect::default(), } } @@ -429,6 +437,36 @@ where ) } + fn handle_mouse_events(&mut self, mouse_event: MouseEvent) { + let pos = Position::new(mouse_event.column, mouse_event.row); + let in_popular = self.popular_mangas_area.contains(pos); + + match mouse_event.kind { + MouseEventKind::ScrollUp => { + if in_popular { + self.local_action_tx.send(HomeActions::SelectPreviousPopularManga).ok(); + } else { + self.local_action_tx.send(HomeActions::SelectPreviousRecentlyAddedManga).ok(); + } + }, + MouseEventKind::ScrollDown => { + if in_popular { + self.local_action_tx.send(HomeActions::SelectNextPopularManga).ok(); + } else { + self.local_action_tx.send(HomeActions::SelectNextRecentlyAddedManga).ok(); + } + }, + MouseEventKind::Down(MouseButton::Left) => { + if in_popular { + self.local_action_tx.send(HomeActions::GoToPopularMangaPage).ok(); + } else if self.recently_added_area.contains(pos) { + self.local_action_tx.send(HomeActions::GoToRecentlyAddedMangaPage).ok(); + } + }, + _ => {}, + } + } + pub fn handle_key_events(&mut self, key_event: KeyEvent) { match key_event.code { KeyCode::Char('w') => {