mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-08-03 12:28:13 +00:00
refactor(core): optimize repeated code for swipe directions
This commit is contained in:
parent
fbfb000d62
commit
d1cf36097a
@ -22,6 +22,38 @@ impl SwipeDirection {
|
|||||||
SwipeDirection::Right => Offset::x(size.x),
|
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)]
|
#[derive(Clone)]
|
||||||
|
@ -63,6 +63,36 @@ impl SwipeConfig {
|
|||||||
self[dir].is_some()
|
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> {
|
pub fn duration(&self, dir: SwipeDirection) -> Option<Duration> {
|
||||||
self[dir].as_ref().map(|s| s.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)]
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum SwipeDetectMsg {
|
pub enum SwipeDetectMsg {
|
||||||
Move(SwipeDirection, i16),
|
Move(SwipeDirection, u16),
|
||||||
Trigger(SwipeDirection),
|
Trigger(SwipeDirection),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,17 +149,20 @@ pub struct SwipeDetect {
|
|||||||
origin: Option<Point>,
|
origin: Option<Point>,
|
||||||
locked: Option<SwipeDirection>,
|
locked: Option<SwipeDirection>,
|
||||||
final_animation: Option<Animation<i16>>,
|
final_animation: Option<Animation<i16>>,
|
||||||
moved: i16,
|
moved: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SwipeDetect {
|
impl SwipeDetect {
|
||||||
const DISTANCE: i16 = 120;
|
const DISTANCE: u16 = 120;
|
||||||
pub const PROGRESS_MAX: i16 = 1000;
|
pub const PROGRESS_MAX: i16 = 1000;
|
||||||
|
|
||||||
const DURATION_MS: u32 = 333;
|
const DURATION_MS: u32 = 333;
|
||||||
const TRIGGER_THRESHOLD: f32 = 0.3;
|
const TRIGGER_THRESHOLD: f32 = 0.3;
|
||||||
const DETECT_THRESHOLD: f32 = 0.1;
|
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 {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
origin: None,
|
origin: None,
|
||||||
@ -139,16 +172,16 @@ impl SwipeDetect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn min_lock(&self) -> i16 {
|
const fn min_lock(&self) -> u16 {
|
||||||
(Self::DISTANCE as f32 * Self::DETECT_THRESHOLD) as i16
|
Self::MIN_LOCK
|
||||||
}
|
}
|
||||||
|
|
||||||
fn min_trigger(&self) -> i16 {
|
const fn min_trigger(&self) -> u16 {
|
||||||
(Self::DISTANCE as f32 * Self::TRIGGER_THRESHOLD) as i16
|
Self::MIN_TRIGGER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn progress(&self, val: i16) -> i16 {
|
fn progress(&self, val: u16) -> u16 {
|
||||||
((val.max(0) as f32 / Self::DISTANCE as f32) * Self::PROGRESS_MAX as f32) as i16
|
((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) {
|
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
|
// Compare the touch distance with our allowed directions and determine if it
|
||||||
// constitutes a valid swipe.
|
// constitutes a valid swipe.
|
||||||
let ofs = pos - origin;
|
let ofs = pos - origin;
|
||||||
let ofs_min = ofs.abs() - Offset::new(self.min_lock(), self.min_lock());
|
|
||||||
|
|
||||||
let mut res = None;
|
let res = match self.locked {
|
||||||
if self.locked.is_none() {
|
Some(locked) => {
|
||||||
if ofs.x > 0 && ofs_min.x > 0 && config.is_allowed(SwipeDirection::Right) {
|
// advance in locked direction only
|
||||||
self.locked = Some(SwipeDirection::Right);
|
let moved = config.progress(locked, ofs, self.min_lock());
|
||||||
res = Some(SwipeDetectMsg::Move(
|
Some(SwipeDetectMsg::Move(locked, self.progress(moved)))
|
||||||
SwipeDirection::Right,
|
|
||||||
self.progress(ofs_min.x),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if ofs.x < 0 && ofs_min.x > 0 && config.is_allowed(SwipeDirection::Left) {
|
None => {
|
||||||
self.locked = Some(SwipeDirection::Left);
|
let mut res = None;
|
||||||
res = Some(SwipeDetectMsg::Move(
|
for dir in SwipeDirection::iter() {
|
||||||
SwipeDirection::Left,
|
let progress = config.progress(dir, ofs, self.min_lock());
|
||||||
self.progress(ofs_min.x),
|
if progress > 0 {
|
||||||
));
|
self.locked = Some(dir);
|
||||||
}
|
res = Some(SwipeDetectMsg::Move(dir, self.progress(progress)));
|
||||||
if ofs.y < 0 && ofs_min.y > 0 && config.is_allowed(SwipeDirection::Up) {
|
break;
|
||||||
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),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SwipeDirection::Right => {
|
res
|
||||||
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),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Todo trigger an action if distance is met
|
// Todo trigger an action if distance is met
|
||||||
|
|
||||||
if let Some(SwipeDetectMsg::Move(_, ofs)) = res {
|
if let Some(SwipeDetectMsg::Move(_, progress)) = res {
|
||||||
self.moved = ofs;
|
self.moved = progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
if animation_disabled() {
|
if animation_disabled() {
|
||||||
@ -289,27 +266,32 @@ impl SwipeDetect {
|
|||||||
// Compare the touch distance with our allowed directions and determine if it
|
// Compare the touch distance with our allowed directions and determine if it
|
||||||
// constitutes a valid swipe.
|
// constitutes a valid swipe.
|
||||||
let ofs = pos - origin;
|
let ofs = pos - origin;
|
||||||
let ofs_min = ofs.abs() - Offset::new(self.min_trigger(), self.min_trigger());
|
|
||||||
|
|
||||||
match self.locked {
|
let final_value = match self.locked {
|
||||||
// advance in locked direction only
|
// advance in locked direction only trigger animation towards ending
|
||||||
Some(locked) if config.progress(locked, ofs, 0) > 0 => (),
|
// position
|
||||||
// advance in direction other than locked clears the lock -- touch ends
|
Some(locked) if config.progress(locked, ofs, self.min_trigger()) > 0 => {
|
||||||
// without triggering
|
Self::PROGRESS_MAX
|
||||||
Some(_) => self.locked = None,
|
}
|
||||||
|
// advance in direction other than locked trigger animation towards starting
|
||||||
|
// position
|
||||||
|
Some(_) => 0,
|
||||||
None => {
|
None => {
|
||||||
|
let mut res = 0;
|
||||||
for dir in SwipeDirection::iter() {
|
for dir in SwipeDirection::iter() {
|
||||||
// insta-lock if the movement went at least the trigger distance
|
// insta-lock if the movement went at least the trigger distance
|
||||||
if config.progress(dir, ofs, self.min_trigger()) > 0 {
|
if config.progress(dir, ofs, self.min_trigger()) > 0 {
|
||||||
self.locked = Some(dir);
|
self.locked = Some(dir);
|
||||||
break;
|
res = Self::PROGRESS_MAX;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(locked) = self.locked else {
|
let Some(locked) = self.locked else {
|
||||||
// No direction is locked. Touch ended without triggering a swipe.
|
// Touch ended without triggering a swipe.
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -327,7 +309,7 @@ impl SwipeDetect {
|
|||||||
let duration = ((duration.to_millis() as f32 * ratio) as u32).max(0);
|
let duration = ((duration.to_millis() as f32 * ratio) as u32).max(0);
|
||||||
self.final_animation = Some(Animation::new(
|
self.final_animation = Some(Animation::new(
|
||||||
self.moved as i16,
|
self.moved as i16,
|
||||||
Self::PROGRESS_MAX,
|
final_value,
|
||||||
Duration::from_millis(duration),
|
Duration::from_millis(duration),
|
||||||
Instant::now(),
|
Instant::now(),
|
||||||
));
|
));
|
||||||
@ -338,51 +320,19 @@ impl SwipeDetect {
|
|||||||
self.locked = None;
|
self.locked = None;
|
||||||
return Some(SwipeDetectMsg::Trigger(locked));
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Event::Timer(EventCtx::ANIM_FRAME_TIMER), _) => {
|
(Event::Timer(EventCtx::ANIM_FRAME_TIMER), _) => {
|
||||||
if self.locked.is_some() {
|
if let Some(locked) = self.locked {
|
||||||
let mut finish = false;
|
let mut finish = false;
|
||||||
let res = if let Some(animation) = &self.final_animation {
|
let res = if let Some(animation) = &self.final_animation {
|
||||||
if animation.finished(Instant::now()) {
|
if animation.finished(Instant::now()) {
|
||||||
finish = true;
|
finish = true;
|
||||||
if animation.to != 0 {
|
if animation.to != 0 {
|
||||||
Some(SwipeDetectMsg::Trigger(self.locked.unwrap()))
|
Some(SwipeDetectMsg::Trigger(locked))
|
||||||
} else {
|
} else {
|
||||||
Some(SwipeDetectMsg::Move(self.locked.unwrap(), 0))
|
Some(SwipeDetectMsg::Move(locked, 0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx.request_anim_frame();
|
ctx.request_anim_frame();
|
||||||
@ -391,8 +341,8 @@ impl SwipeDetect {
|
|||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(SwipeDetectMsg::Move(
|
Some(SwipeDetectMsg::Move(
|
||||||
self.locked.unwrap(),
|
locked,
|
||||||
animation.value(Instant::now()),
|
animation.value(Instant::now()).max(0) as u16,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,7 @@ impl<Q: FlowState, S: FlowStore> Component for SwipeFlow<Q, S> {
|
|||||||
Some(Event::Swipe(SwipeEvent::End(dir)))
|
Some(Event::Swipe(SwipeEvent::End(dir)))
|
||||||
}
|
}
|
||||||
Some(SwipeDetectMsg::Move(dir, progress)) => {
|
Some(SwipeDetectMsg::Move(dir, progress)) => {
|
||||||
Some(Event::Swipe(SwipeEvent::Move(dir, progress)))
|
Some(Event::Swipe(SwipeEvent::Move(dir, progress as i16)))
|
||||||
}
|
}
|
||||||
_ => Some(event),
|
_ => Some(event),
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ impl<T: Swipable + Component> Component for SwipeUpScreen<T> {
|
|||||||
return Some(SwipeUpScreenMsg::Swiped);
|
return Some(SwipeUpScreenMsg::Swiped);
|
||||||
}
|
}
|
||||||
Some(SwipeDetectMsg::Move(dir, progress)) => {
|
Some(SwipeDetectMsg::Move(dir, progress)) => {
|
||||||
Event::Swipe(SwipeEvent::Move(dir, progress))
|
Event::Swipe(SwipeEvent::Move(dir, progress as i16))
|
||||||
}
|
}
|
||||||
_ => event,
|
_ => event,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user