From f31e07f706a7e3f3cc03e2e7d3385416fc751d7e Mon Sep 17 00:00:00 2001 From: Miika Alikirri Date: Tue, 31 Jan 2023 22:08:43 +0200 Subject: [PATCH] Add mouse interaction to Yle Image Gui * Mouse will point to links on hover * New page will be loaded when mouse is clicked while hovering --- src/gui/common.rs | 18 ++++++++++++------ src/gui/mod.rs | 5 +---- src/gui/yle_image.rs | 37 +++++++++++++++++++++++++++++++------ src/gui/yle_text.rs | 2 +- 4 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/gui/common.rs b/src/gui/common.rs index a7ca536..01a5e2a 100644 --- a/src/gui/common.rs +++ b/src/gui/common.rs @@ -6,7 +6,7 @@ use std::{ time::Duration, }; -use egui::{self, InputState, Key::*}; +use egui::{self, InputState, Key::*, PointerState}; use crate::parser::{HtmlItem, HtmlLink, HtmlLoader, HtmlParser}; @@ -183,7 +183,7 @@ pub enum FetchState { } pub trait IGuiCtx { - fn handle_input(&mut self, input: &InputState); + fn handle_input(&mut self, input: InputState); fn draw(&mut self, ui: &mut egui::Ui); fn set_refresh_interval(&mut self, interval: u64); fn stop_refresh_interval(&mut self); @@ -199,6 +199,7 @@ pub struct GuiContext { pub history: TeleHistory, pub page_buffer: Vec, pub worker: Option, + pub pointer: PointerState, } impl GuiContext { @@ -212,6 +213,7 @@ impl GuiContext { page_buffer: Vec::with_capacity(3), history: TeleHistory::new(current_page), worker: None, + pointer: Default::default(), } } @@ -230,17 +232,18 @@ impl GuiContext { page_buffer: Vec::with_capacity(3), history: TeleHistory::new(current_page), worker: None, + pointer: Default::default(), } } - pub fn handle_input(&mut self, input: &InputState) { + pub fn handle_input(&mut self, input: InputState) { // Ignore input while fetching match *self.state.lock().unwrap() { FetchState::Complete(_) => {} _ => return, }; - if let Some(num) = input_to_num(input) { + if let Some(num) = input_to_num(&input) { if self.page_buffer.len() < 3 { self.page_buffer.push(num); } @@ -252,8 +255,11 @@ impl GuiContext { } } + // After keyboard stuff is handled, move the ownership of pointer to self and + // deal with mouse inputs + self.pointer = input.pointer; // prev - if input.pointer.button_released(egui::PointerButton::Extra1) { + if self.pointer.button_released(egui::PointerButton::Extra1) { if let Some(page) = self.history.prev() { self.current_page = page; self.load_current_page(); @@ -261,7 +267,7 @@ impl GuiContext { } // next - if input.pointer.button_released(egui::PointerButton::Extra2) { + if self.pointer.button_released(egui::PointerButton::Extra2) { if let Some(page) = self.history.next() { self.current_page = page; self.load_current_page(); diff --git a/src/gui/mod.rs b/src/gui/mod.rs index be249c9..61812b9 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -188,9 +188,6 @@ impl TeleTextApp { TeleTextSettings::default() }; - /* let mut page = Box::new(GuiYleImageContext::new(GuiContext::new( - ctx.egui_ctx.clone(), - ))) as Box; */ let mut page = settings.open_page.to_gui(&ctx.egui_ctx); let page_ref = &mut page as &mut Box; @@ -226,7 +223,7 @@ impl eframe::App for TeleTextApp { egui::CentralPanel::default().show(ctx, |ui| { if let Some(page) = page { - page.handle_input(&input); + page.handle_input(input); page.draw(ui); } }); diff --git a/src/gui/yle_image.rs b/src/gui/yle_image.rs index a27726e..9002c06 100644 --- a/src/gui/yle_image.rs +++ b/src/gui/yle_image.rs @@ -1,9 +1,9 @@ use std::{cell::RefCell, ops::Deref, rc::Rc}; -use egui::{FontId, InputState, RichText, TextStyle}; +use egui::{CursorIcon, FontId, InputState, RichText, TextStyle}; use egui_extras::RetainedImage; -use crate::parser::{HtmlLink, HtmlText, YleImage}; +use crate::parser::{common::HtmlImageArea, HtmlLink, HtmlText, YleImage}; use super::common::{FetchState, GuiContext, IGuiCtx, PageDraw, TelePage, TelePager}; @@ -82,11 +82,36 @@ impl<'a> GuiYleImage<'a> { } } - fn draw_image(&mut self, image: &[u8]) { + fn draw_image(&mut self, image: &[u8], image_map: &Vec) { + let mut ctx = self.ctx.borrow_mut(); + let pos = ctx.pointer.hover_pos(); + let clicked = ctx.pointer.primary_released(); self.ui .with_layout(egui::Layout::top_down(egui::Align::Center), |ui| { let image = RetainedImage::from_image_bytes("debug_name", image).unwrap(); - image.show_max_size(ui, ui.available_size()); + + let resp = image.show_max_size(ui, ui.available_size()); + if let Some(pos) = pos { + let rh = resp.rect.max.y - resp.rect.min.y; + let rw = resp.rect.max.x - resp.rect.min.x; + // The aspect ratio of the image will stay the same as it's being scaled + // so the scale of width and height will be the same + let scale = rw / (image.size()[0] as f32); + // Translate the pointer to be inside of the image + let px = pos.x - resp.rect.min.x; + let py = pos.y - resp.rect.min.y; + if px > 0.0 && px < rw && py > 0.0 && py < rh { + for area in image_map { + if area.in_area(px, py, scale) { + ui.ctx().output().cursor_icon = CursorIcon::PointingHand; + if clicked { + ctx.load_page(&area.link, true); + } + break; + } + } + } + } }); } @@ -165,7 +190,7 @@ impl<'a> PageDraw<'a, YleImage> for GuiYleImage<'a> { match state.lock().unwrap().deref() { FetchState::Complete(page) => { self.draw_header(&page.title); - self.draw_image(&page.image); + self.draw_image(&page.image, &page.image_map); self.draw_page_navigation(&page.botton_navigation); } FetchState::Fetching => { @@ -231,7 +256,7 @@ impl GuiYleImageContext { } impl IGuiCtx for GuiYleImageContext { - fn handle_input(&mut self, input: &InputState) { + fn handle_input(&mut self, input: InputState) { self.ctx.handle_input(input) } diff --git a/src/gui/yle_text.rs b/src/gui/yle_text.rs index 6ccbb81..3080558 100644 --- a/src/gui/yle_text.rs +++ b/src/gui/yle_text.rs @@ -284,7 +284,7 @@ impl GuiYleTextContext { } impl IGuiCtx for GuiYleTextContext { - fn handle_input(&mut self, input: &InputState) { + fn handle_input(&mut self, input: InputState) { self.ctx.handle_input(input) }