1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-22 22:38:08 +00:00

refactor(core): optimize repeated code for swipe directions

This commit is contained in:
matejcik 2024-06-04 16:07:09 +02:00 committed by matejcik
parent fbfb000d62
commit d1cf36097a
4 changed files with 114 additions and 132 deletions

View File

@ -22,6 +22,38 @@ impl SwipeDirection {
SwipeDirection::Right => Offset::x(size.x),
}
}
pub fn iter() -> SwipeDirectionIterator {
SwipeDirectionIterator::new()
}
}
pub struct SwipeDirectionIterator {
current: Option<SwipeDirection>,
}
impl SwipeDirectionIterator {
pub fn new() -> Self {
SwipeDirectionIterator {
current: Some(SwipeDirection::Up),
}
}
}
impl Iterator for SwipeDirectionIterator {
type Item = SwipeDirection;
fn next(&mut self) -> Option<Self::Item> {
let next_state = match self.current {
Some(SwipeDirection::Up) => Some(SwipeDirection::Down),
Some(SwipeDirection::Down) => Some(SwipeDirection::Left),
Some(SwipeDirection::Left) => Some(SwipeDirection::Right),
Some(SwipeDirection::Right) => None,
None => None,
};
core::mem::replace(&mut self.current, next_state)
}
}
#[derive(Clone)]

View File

@ -63,6 +63,36 @@ impl SwipeConfig {
self[dir].is_some()
}
/// Calculate how much progress over `threshold` was made in the swipe
/// direction.
///
/// If the swipe direction is not allowed, this will return 0.
pub fn progress(&self, dir: SwipeDirection, movement: Offset, threshold: u16) -> u16 {
if !self.is_allowed(dir) {
return 0;
}
let correct_movement = match dir {
SwipeDirection::Right => movement.x > 0,
SwipeDirection::Left => movement.x < 0,
SwipeDirection::Down => movement.y > 0,
SwipeDirection::Up => movement.y < 0,
};
if !correct_movement {
return 0;
}
let movement = movement.abs();
match dir {
SwipeDirection::Right => (movement.x as u16).saturating_sub(threshold),
SwipeDirection::Left => (movement.x as u16).saturating_sub(threshold),
SwipeDirection::Down => (movement.y as u16).saturating_sub(threshold),
SwipeDirection::Up => (movement.y as u16).saturating_sub(threshold),
}
}
pub fn duration(&self, dir: SwipeDirection) -> Option<Duration> {
self[dir].as_ref().map(|s| s.duration)
}
@ -111,7 +141,7 @@ impl core::ops::IndexMut<SwipeDirection> for SwipeConfig {
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum SwipeDetectMsg {
Move(SwipeDirection, i16),
Move(SwipeDirection, u16),
Trigger(SwipeDirection),
}
@ -119,17 +149,20 @@ pub struct SwipeDetect {
origin: Option<Point>,
locked: Option<SwipeDirection>,
final_animation: Option<Animation<i16>>,
moved: i16,
moved: u16,
}
impl SwipeDetect {
const DISTANCE: i16 = 120;
const DISTANCE: u16 = 120;
pub const PROGRESS_MAX: i16 = 1000;
const DURATION_MS: u32 = 333;
const TRIGGER_THRESHOLD: f32 = 0.3;
const DETECT_THRESHOLD: f32 = 0.1;
const MIN_LOCK: u16 = (Self::DISTANCE as f32 * Self::DETECT_THRESHOLD) as u16;
const MIN_TRIGGER: u16 = (Self::DISTANCE as f32 * Self::TRIGGER_THRESHOLD) as u16;
pub fn new() -> Self {
Self {
origin: None,
@ -139,16 +172,16 @@ impl SwipeDetect {
}
}
fn min_lock(&self) -> i16 {
(Self::DISTANCE as f32 * Self::DETECT_THRESHOLD) as i16
const fn min_lock(&self) -> u16 {
Self::MIN_LOCK
}
fn min_trigger(&self) -> i16 {
(Self::DISTANCE as f32 * Self::TRIGGER_THRESHOLD) as i16
const fn min_trigger(&self) -> u16 {
Self::MIN_TRIGGER
}
fn progress(&self, val: i16) -> i16 {
((val.max(0) as f32 / Self::DISTANCE as f32) * Self::PROGRESS_MAX as f32) as i16
fn progress(&self, val: u16) -> u16 {
((val as f32 / Self::DISTANCE as f32) * Self::PROGRESS_MAX as f32) as u16
}
pub fn trigger(&mut self, ctx: &mut EventCtx, dir: SwipeDirection, config: SwipeConfig) {
@ -191,87 +224,31 @@ impl SwipeDetect {
// Compare the touch distance with our allowed directions and determine if it
// constitutes a valid swipe.
let ofs = pos - origin;
let ofs_min = ofs.abs() - Offset::new(self.min_lock(), self.min_lock());
let mut res = None;
if self.locked.is_none() {
if ofs.x > 0 && ofs_min.x > 0 && config.is_allowed(SwipeDirection::Right) {
self.locked = Some(SwipeDirection::Right);
res = Some(SwipeDetectMsg::Move(
SwipeDirection::Right,
self.progress(ofs_min.x),
));
let res = match self.locked {
Some(locked) => {
// advance in locked direction only
let moved = config.progress(locked, ofs, self.min_lock());
Some(SwipeDetectMsg::Move(locked, self.progress(moved)))
}
if ofs.x < 0 && ofs_min.x > 0 && config.is_allowed(SwipeDirection::Left) {
self.locked = Some(SwipeDirection::Left);
res = Some(SwipeDetectMsg::Move(
SwipeDirection::Left,
self.progress(ofs_min.x),
));
}
if ofs.y < 0 && ofs_min.y > 0 && config.is_allowed(SwipeDirection::Up) {
self.locked = Some(SwipeDirection::Up);
res = Some(SwipeDetectMsg::Move(
SwipeDirection::Up,
self.progress(ofs_min.y),
));
}
if ofs.y > 0 && ofs_min.y > 0 && config.is_allowed(SwipeDirection::Down) {
self.locked = Some(SwipeDirection::Down);
res = Some(SwipeDetectMsg::Move(
SwipeDirection::Down,
self.progress(ofs_min.y),
));
}
} else {
res = match self.locked.unwrap() {
SwipeDirection::Left => {
if ofs.x > 0 {
Some(SwipeDetectMsg::Move(SwipeDirection::Left, 0))
} else {
Some(SwipeDetectMsg::Move(
SwipeDirection::Left,
self.progress(ofs_min.x),
))
None => {
let mut res = None;
for dir in SwipeDirection::iter() {
let progress = config.progress(dir, ofs, self.min_lock());
if progress > 0 {
self.locked = Some(dir);
res = Some(SwipeDetectMsg::Move(dir, self.progress(progress)));
break;
}
}
SwipeDirection::Right => {
if ofs.x < 0 {
Some(SwipeDetectMsg::Move(SwipeDirection::Right, 0))
} else {
Some(SwipeDetectMsg::Move(
SwipeDirection::Right,
self.progress(ofs_min.x),
))
}
}
SwipeDirection::Up => {
if ofs.y > 0 {
Some(SwipeDetectMsg::Move(SwipeDirection::Up, 0))
} else {
Some(SwipeDetectMsg::Move(
SwipeDirection::Up,
self.progress(ofs_min.y),
))
}
}
SwipeDirection::Down => {
if ofs.y < 0 {
Some(SwipeDetectMsg::Move(SwipeDirection::Down, 0))
} else {
Some(SwipeDetectMsg::Move(
SwipeDirection::Down,
self.progress(ofs_min.y),
))
}
}
};
}
res
}
};
// Todo trigger an action if distance is met
if let Some(SwipeDetectMsg::Move(_, ofs)) = res {
self.moved = ofs;
if let Some(SwipeDetectMsg::Move(_, progress)) = res {
self.moved = progress;
}
if animation_disabled() {
@ -289,27 +266,32 @@ impl SwipeDetect {
// Compare the touch distance with our allowed directions and determine if it
// constitutes a valid swipe.
let ofs = pos - origin;
let ofs_min = ofs.abs() - Offset::new(self.min_trigger(), self.min_trigger());
match self.locked {
// advance in locked direction only
Some(locked) if config.progress(locked, ofs, 0) > 0 => (),
// advance in direction other than locked clears the lock -- touch ends
// without triggering
Some(_) => self.locked = None,
let final_value = match self.locked {
// advance in locked direction only trigger animation towards ending
// position
Some(locked) if config.progress(locked, ofs, self.min_trigger()) > 0 => {
Self::PROGRESS_MAX
}
// advance in direction other than locked trigger animation towards starting
// position
Some(_) => 0,
None => {
let mut res = 0;
for dir in SwipeDirection::iter() {
// insta-lock if the movement went at least the trigger distance
if config.progress(dir, ofs, self.min_trigger()) > 0 {
self.locked = Some(dir);
break;
res = Self::PROGRESS_MAX;
}
}
res
}
};
let Some(locked) = self.locked else {
// No direction is locked. Touch ended without triggering a swipe.
// Touch ended without triggering a swipe.
return None;
};
@ -327,7 +309,7 @@ impl SwipeDetect {
let duration = ((duration.to_millis() as f32 * ratio) as u32).max(0);
self.final_animation = Some(Animation::new(
self.moved as i16,
Self::PROGRESS_MAX,
final_value,
Duration::from_millis(duration),
Instant::now(),
));
@ -338,51 +320,19 @@ impl SwipeDetect {
self.locked = None;
return Some(SwipeDetectMsg::Trigger(locked));
}
if finalize {
if !animation_disabled() {
ctx.request_anim_frame();
ctx.request_paint();
let done = self.moved as f32 / Self::PROGRESS_MAX as f32;
let ratio = 1.0 - done;
let duration = config
.duration(self.locked.unwrap())
.unwrap_or(Duration::from_millis(Self::DURATION_MS));
let duration = ((duration.to_millis() as f32 * ratio) as u32).max(0);
self.final_animation = Some(Animation::new(
self.moved,
final_value,
Duration::from_millis(duration),
Instant::now(),
));
} else {
ctx.request_anim_frame();
ctx.request_paint();
self.final_animation = None;
self.moved = 0;
let locked = self.locked.take();
if final_value != 0 {
return Some(SwipeDetectMsg::Trigger(locked.unwrap()));
}
}
}
return None;
}
}
(Event::Timer(EventCtx::ANIM_FRAME_TIMER), _) => {
if self.locked.is_some() {
if let Some(locked) = self.locked {
let mut finish = false;
let res = if let Some(animation) = &self.final_animation {
if animation.finished(Instant::now()) {
finish = true;
if animation.to != 0 {
Some(SwipeDetectMsg::Trigger(self.locked.unwrap()))
Some(SwipeDetectMsg::Trigger(locked))
} else {
Some(SwipeDetectMsg::Move(self.locked.unwrap(), 0))
Some(SwipeDetectMsg::Move(locked, 0))
}
} else {
ctx.request_anim_frame();
@ -391,8 +341,8 @@ impl SwipeDetect {
None
} else {
Some(SwipeDetectMsg::Move(
self.locked.unwrap(),
animation.value(Instant::now()),
locked,
animation.value(Instant::now()).max(0) as u16,
))
}
}

View File

@ -170,7 +170,7 @@ impl<Q: FlowState, S: FlowStore> Component for SwipeFlow<Q, S> {
Some(Event::Swipe(SwipeEvent::End(dir)))
}
Some(SwipeDetectMsg::Move(dir, progress)) => {
Some(Event::Swipe(SwipeEvent::Move(dir, progress)))
Some(Event::Swipe(SwipeEvent::Move(dir, progress as i16)))
}
_ => Some(event),
}

View File

@ -49,7 +49,7 @@ impl<T: Swipable + Component> Component for SwipeUpScreen<T> {
return Some(SwipeUpScreenMsg::Swiped);
}
Some(SwipeDetectMsg::Move(dir, progress)) => {
Event::Swipe(SwipeEvent::Move(dir, progress))
Event::Swipe(SwipeEvent::Move(dir, progress as i16))
}
_ => event,
};