wallet: chatview temporary finger scrolling impl

This commit is contained in:
darkfi
2024-09-06 09:12:08 +02:00
parent 73076d45fc
commit 780d239e15
4 changed files with 104 additions and 16 deletions

View File

@@ -662,7 +662,11 @@ pub(super) async fn make(app: &App) {
#[cfg(target_os = "linux")]
node.set_property_f32(Role::App, "mouse_scroll_start_accel", 4.).unwrap();
#[cfg(target_os = "android")]
node.set_property_f32(Role::App, "mouse_scroll_decel", 0.9).unwrap();
#[cfg(target_os = "linux")]
node.set_property_f32(Role::App, "mouse_scroll_decel", 0.5).unwrap();
node.set_property_f32(Role::App, "mouse_scroll_resist", 0.9).unwrap();
let prop = node.get_property("timestamp_color").unwrap();

View File

@@ -44,6 +44,7 @@ mod net;
mod prop;
mod pubsub;
//mod py;
mod ringbuf;
mod scene;
mod text;
mod ui;

View File

@@ -0,0 +1,62 @@
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2024 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#[derive(Clone)]
pub struct RingBuffer<T, const N: usize> {
vals: [Option<T>; N],
head: i64,
tail: i64
}
impl<T, const N: usize> RingBuffer<T, N> {
const LEN: usize = N;
pub fn new() -> Self {
Self { vals: [const { None }; N], head: -1, tail: -1 }
}
pub fn push(&mut self, v: T) {
let len = Self::LEN as i64;
self.head = (self.head + 1) % len;
if self.head == self.tail {
self.tail = (self.tail + 1) % len;
}
if self.tail < 0 {
self.tail = 0;
}
let _ = std::mem::replace(&mut self.vals[self.head as usize], Some(v));
}
pub fn head(&self) -> Option<&T> {
if self.head < 0 {
return None
}
Some(self.vals[self.head as usize].as_ref().unwrap())
}
pub fn tail(&self) -> Option<&T> {
if self.tail < 0 {
return None
}
Some(self.vals[self.tail as usize].as_ref().unwrap())
}
}

View File

@@ -46,6 +46,7 @@ use crate::{
mesh::{Color, MeshBuilder, COLOR_BLUE, COLOR_GREEN},
prop::{PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyUint32, Role},
pubsub::Subscription,
ringbuf::RingBuffer,
scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
text::{self, Glyph, GlyphPositionIter, TextShaperPtr},
util::{enumerate, is_whitespace},
@@ -86,6 +87,10 @@ struct TouchInfo {
start_scroll: f32,
start_y: f32,
start_instant: std::time::Instant,
/// Used for flick scrolling
samples: RingBuffer<(std::time::Instant, f32), 2>,
last_instant: std::time::Instant,
last_y: f32,
}
@@ -96,6 +101,7 @@ impl TouchInfo {
start_scroll: 0.,
start_y: 0.,
start_instant: std::time::Instant::now(),
samples: RingBuffer::new(),
last_instant: std::time::Instant::now(),
last_y: 0.,
}
@@ -474,6 +480,8 @@ impl ChatView {
touch_info.start_scroll = self.scroll.get();
touch_info.start_y = touch_y;
touch_info.start_instant = std::time::Instant::now();
touch_info.samples = RingBuffer::new();
touch_info.samples.push((std::time::Instant::now(), touch_y));
touch_info.last_instant = std::time::Instant::now();
touch_info.last_y = touch_y;
}
@@ -485,11 +493,19 @@ impl ChatView {
let (start_scroll, start_y, do_update) = {
let mut touch_info = self.touch_info.lock().unwrap();
touch_info.last_y = touch_y;
let last_sample = touch_info.samples.head().unwrap().0.elapsed().as_millis_f32();
// Sample every 10ms
if last_sample > 10. {
touch_info.samples.push((std::time::Instant::now(), touch_y));
}
let last_elapsed = touch_info.last_instant.elapsed().as_millis_f32();
let do_update = last_elapsed > 20.;
if do_update {
touch_info.last_instant = std::time::Instant::now();
}
(touch_info.start_scroll, touch_info.start_y, do_update)
};
@@ -500,7 +516,7 @@ impl ChatView {
// how often we move to fixed intervals.
// draw a poly shape and eval each line segment.
let scroll = start_scroll + dist;
let instant = std::time::Instant::now();
if do_update {
self.scrollview(scroll).await;
}
@@ -511,10 +527,17 @@ impl ChatView {
// Now calculate scroll acceleration
let touch_info = self.touch_info.lock().unwrap().clone();
let time = touch_info.start_instant.elapsed().as_millis_f32();
let dist = touch_y - touch_info.start_y;
let (oldest_sample, sample_y) = touch_info.samples.head().unwrap();
//let Some((oldest_sample, sample_y)) = touch_info.samples.head() else { return };
let time = oldest_sample.elapsed().as_millis_f32();
let dist = touch_y - sample_y;
//let speed = dist / time;
//self.speed.fetch_add(speed, Ordering::Relaxed);
//debug!(target: "ui::chatview", "speed = {dist} / {time} = {speed}");
let accel = self.mouse_scroll_start_accel.get() * dist / time;
debug!(target: "ui::chatview", "accel = {dist} / {time} = {accel}");
self.accel.fetch_add(accel, Ordering::Relaxed);
self.motion_cv.notify();
}
@@ -574,17 +597,18 @@ impl ChatView {
async fn handle_movement(&self) {
loop {
msleep(20).await;
let mut accel = self.accel.load(Ordering::Relaxed);
let mut speed = self.speed.fetch_add(accel, Ordering::Relaxed) + accel;
// Apply resistance to acceleration
accel *= self.mouse_scroll_decel.get();
if accel.abs() < 0.05 {
accel = 0.;
}
self.accel.store(accel, Ordering::Relaxed);
// Apply constant decel to speed
if is_zero(accel) {
// Apply constant decel to speed
speed *= self.mouse_scroll_resist.get();
if speed.abs() < BIG_EPSILON {
speed = 0.;
@@ -597,25 +621,22 @@ impl ChatView {
return
}
if is_zero(speed) {
self.accel.store(0., Ordering::Relaxed);
self.speed.store(0., Ordering::Relaxed);
return
}
let scroll = self.scroll.get() + speed;
let dist = self.scrollview(scroll).await;
// We reached the end so just stop
if is_zero(dist) {
self.accel.store(0., Ordering::Relaxed);
self.speed.store(0., Ordering::Relaxed);
return
}
msleep(20).await;
}
}
async fn handle_bgload(&self) {
debug!(target: "ui::chatview", "ChatView::handle_bgload()");
//debug!(target: "ui::chatview", "ChatView::handle_bgload()");
// Do we need to load some more?
let scroll = self.scroll.get();
let mut rect = read_rect(self.rect.clone()).expect("bad rect property");
@@ -628,20 +649,20 @@ impl ChatView {
let total_height = msgbuf.calc_total_height().await;
if total_height > top + preload_height {
// Nothing to do here
debug!(target: "ui::chatview", "bgloader: buffer is sufficient");
//debug!(target: "ui::chatview", "bgloader: buffer is sufficient");
return
}
// Keep loading until this is below 0
let mut remaining_load_height = top + preload_height - total_height;
debug!(target: "ui::chatview", "bgloader: remaining px = {remaining_load_height}");
//debug!(target: "ui::chatview", "bgloader: remaining px = {remaining_load_height}");
let mut remaining_visible = top - total_height;
// Get the current earliest timestamp
let iter = match msgbuf.oldest_timestamp() {
Some(oldest_timest) => {
// iterate from there
debug!(target: "ui::chatview", "preloading from {oldest_timest}");
//debug!(target: "ui::chatview", "preloading from {oldest_timest}");
let timest = (oldest_timest - 1).to_be_bytes();
let mut key = [0u8; 8 + 32];
key[..8].clone_from_slice(&timest);
@@ -650,7 +671,7 @@ impl ChatView {
iter
}
None => {
debug!(target: "ui::chatview", "initial load");
//debug!(target: "ui::chatview", "initial load");
self.tree.iter().rev()
}
};