skipped 6 lines 7 7 8 8 use std::borrow::Cow; 9 9 use std::cmp::{max, min, Ordering}; 10 + use std::collections::HashSet; 10 11 use std::ffi::OsStr; 11 12 use std::fmt::Debug; 12 13 use std::marker::PhantomData; 14 + use std::mem; 13 15 use std::time::{Duration, Instant}; 14 16 15 17 use winit::dpi::PhysicalPosition; 16 18 use winit::event::{ 17 - ElementState, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase , 19 + ElementState, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, 20 + Touch as TouchEvent, TouchPhase, 18 21 }; 19 22 use winit::event_loop::EventLoopWindowTarget; 20 23 #[cfg(target_os = "macos")] skipped 14 lines 35 38 use crate::display::hint::HintMatch; 36 39 use crate::display::window::Window; 37 40 use crate::display::{Display, SizeInfo}; 38 - use crate::event::{ClickState, Event, EventType, Mouse, TYPING_SEARCH_DELAY}; 41 + use crate::event::{ 42 + ClickState, Event, EventType, Mouse, TouchPurpose, TouchZoom, TYPING_SEARCH_DELAY, 43 + }; 39 44 use crate::message_bar::{self, Message}; 40 45 use crate::scheduler::{Scheduler, TimerId, Topic}; 41 46 skipped 8 lines 50 55 51 56 /// Number of pixels for increasing the selection scrolling speed factor by one. 52 57 const SELECTION_SCROLLING_STEP: f64 = 20.; 58 + 59 + /// Touch scroll speed. 60 + const TOUCH_SCROLL_FACTOR: f64 = 0.35; 61 + 62 + /// Distance before a touch input is considered a drag. 63 + const MAX_TAP_DISTANCE: f64 = 20.; 53 64 54 65 /// Processes input from winit. 55 66 /// skipped 16 lines 72 83 fn selection_is_empty(&self) -> bool; 73 84 fn mouse_mut(&mut self) -> &mut Mouse; 74 85 fn mouse(&self) -> &Mouse; 86 + fn touch_purpose(&mut self) -> &mut TouchPurpose; 75 87 fn received_count(&mut self) -> &mut usize; 76 88 fn suppress_chars(&mut self) -> &mut bool; 77 89 fn modifiers(&mut self) -> &mut ModifiersState; skipped 657 lines 735 747 } 736 748 } 737 749 750 + /// Handle touch input. 751 + pub fn touch(&mut self, touch: TouchEvent) { 752 + match touch.phase { 753 + TouchPhase::Started => self.on_touch_start(touch), 754 + TouchPhase::Moved => self.on_touch_motion(touch), 755 + TouchPhase::Ended | TouchPhase::Cancelled => self.on_touch_end(touch), 756 + } 757 + } 758 + 759 + /// Handle beginning of touch input. 760 + pub fn on_touch_start(&mut self, touch: TouchEvent) { 761 + let touch_purpose = self.ctx.touch_purpose(); 762 + *touch_purpose = match mem::take(touch_purpose) { 763 + TouchPurpose::None => TouchPurpose::Tap(touch), 764 + TouchPurpose::Tap(start) => TouchPurpose::Zoom(TouchZoom::new((start, touch))), 765 + TouchPurpose::Zoom(zoom) => TouchPurpose::Invalid(zoom.slots()), 766 + TouchPurpose::Scroll(event) | TouchPurpose::Select(event) => { 767 + let mut set = HashSet::new(); 768 + set.insert(event.id); 769 + TouchPurpose::Invalid(set) 770 + }, 771 + TouchPurpose::Invalid(mut slots) => { 772 + slots.insert(touch.id); 773 + TouchPurpose::Invalid(slots) 774 + }, 775 + }; 776 + } 777 + 778 + /// Handle touch input movement. 779 + pub fn on_touch_motion(&mut self, touch: TouchEvent) { 780 + let touch_purpose = self.ctx.touch_purpose(); 781 + match touch_purpose { 782 + TouchPurpose::None => (), 783 + // Handle transition from tap to scroll/select. 784 + TouchPurpose::Tap(start) => { 785 + let delta_x = touch.location.x - start.location.x; 786 + let delta_y = touch.location.y - start.location.y; 787 + if delta_x.abs() > MAX_TAP_DISTANCE { 788 + // Update gesture state. 789 + let start_location = start.location; 790 + *touch_purpose = TouchPurpose::Select(*start); 791 + 792 + // Start simulated mouse input. 793 + self.mouse_moved(start_location); 794 + self.mouse_input(ElementState::Pressed, MouseButton::Left); 795 + 796 + // Apply motion since touch start. 797 + self.on_touch_motion(touch); 798 + } else if delta_y.abs() > MAX_TAP_DISTANCE { 799 + // Update gesture state. 800 + *touch_purpose = TouchPurpose::Scroll(*start); 801 + 802 + // Apply motion since touch start. 803 + self.on_touch_motion(touch); 804 + } 805 + }, 806 + TouchPurpose::Zoom(zoom) => { 807 + let font_delta = zoom.font_delta(touch); 808 + self.ctx.change_font_size(font_delta); 809 + }, 810 + TouchPurpose::Scroll(last_touch) => { 811 + // Calculate delta and update last touch position. 812 + let delta_y = touch.location.y - last_touch.location.y; 813 + *touch_purpose = TouchPurpose::Scroll(touch); 814 + 815 + self.scroll_terminal(0., delta_y * TOUCH_SCROLL_FACTOR); 816 + }, 817 + TouchPurpose::Select(_) => self.mouse_moved(touch.location), 818 + TouchPurpose::Invalid(_) => (), 819 + } 820 + } 821 + 822 + /// Handle end of touch input. 823 + pub fn on_touch_end(&mut self, touch: TouchEvent) { 824 + // Finalize the touch motion up to the release point. 825 + self.on_touch_motion(touch); 826 + 827 + let touch_purpose = self.ctx.touch_purpose(); 828 + match touch_purpose { 829 + // Simulate LMB clicks. 830 + TouchPurpose::Tap(start) => { 831 + let start_location = start.location; 832 + *touch_purpose = Default::default(); 833 + 834 + self.mouse_moved(start_location); 835 + self.mouse_input(ElementState::Pressed, MouseButton::Left); 836 + self.mouse_input(ElementState::Released, MouseButton::Left); 837 + }, 838 + // Invalidate zoom once a finger was released. 839 + TouchPurpose::Zoom(zoom) => { 840 + let mut slots = zoom.slots(); 841 + slots.remove(&touch.id); 842 + *touch_purpose = TouchPurpose::Invalid(slots); 843 + }, 844 + // Reset touch state once all slots were released. 845 + TouchPurpose::Invalid(slots) => { 846 + slots.remove(&touch.id); 847 + if slots.is_empty() { 848 + *touch_purpose = Default::default(); 849 + } 850 + }, 851 + // Release simulated LMB. 852 + TouchPurpose::Select(_) => { 853 + *touch_purpose = Default::default(); 854 + self.mouse_input(ElementState::Released, MouseButton::Left); 855 + }, 856 + // Reset touch state on scroll finish. 857 + TouchPurpose::Scroll(_) => *touch_purpose = Default::default(), 858 + TouchPurpose::None => (), 859 + } 860 + } 861 + 738 862 pub fn mouse_input(&mut self, state: ElementState, button: MouseButton) { 739 863 match button { 740 864 MouseButton::Left => self.ctx.mouse_mut().left_button_state = state, skipped 362 lines 1103 1227 #[inline] 1104 1228 fn mouse(&self) -> &Mouse { 1105 1229 self.mouse 1230 + } 1231 + 1232 + #[inline] 1233 + fn touch_purpose(&mut self) -> &mut TouchPurpose { 1234 + unimplemented!(); 1106 1235 } 1107 1236 1108 1237 fn received_count(&mut self) -> &mut usize { skipped 288 lines