Skip to content

Commit 5f28ab1

Browse files
committed
fix members of a multibar always rendering
1 parent f8d33f9 commit 5f28ab1

2 files changed

Lines changed: 103 additions & 8 deletions

File tree

src/draw_target.rs

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,18 @@ impl ProgressDrawTarget {
146146
}
147147
}
148148

149+
/// Check if a call to `drawable` would actually give back a `Drawable`.
150+
pub(crate) fn will_allow_draw(&self, now: Instant) -> bool {
151+
match &self.kind {
152+
TargetKind::Term { rate_limiter, .. } => rate_limiter.will_allow(now),
153+
TargetKind::Multi { state, .. } => state.try_read().unwrap().will_allow_draw(now),
154+
TargetKind::Hidden => false,
155+
TargetKind::TermLike { rate_limiter, .. } => {
156+
rate_limiter.as_ref().map_or(true, |r| r.will_allow(now))
157+
}
158+
}
159+
}
160+
149161
/// Apply the given draw state (draws it).
150162
pub(crate) fn drawable(&mut self, force_draw: bool, now: Instant) -> Option<Drawable<'_>> {
151163
match &mut self.kind {
@@ -169,13 +181,31 @@ impl ProgressDrawTarget {
169181
}
170182
}
171183
TargetKind::Multi { idx, state, .. } => {
172-
let state = state.write().unwrap();
173-
Some(Drawable::Multi {
174-
idx: *idx,
175-
state,
176-
force_draw,
177-
now,
178-
})
184+
let mut state_guard = state.write().unwrap();
185+
186+
// Check if this multibar's inner draw target will rate limit
187+
// the draw call. If so no rendering needs to be done as long
188+
// as we mark the member for redraw. Either case no actual
189+
// request needs to be performed on inner rate limiter otherwise
190+
// the actual draw call won't be able to perform.
191+
if state_guard.will_allow_draw(now) {
192+
state_guard.unmark_delayed(*idx);
193+
194+
let state = MultiState::force_redraw_delayed(state_guard, now)
195+
.ok()
196+
.flatten()
197+
.unwrap_or_else(|| state.write().unwrap());
198+
199+
Some(Drawable::Multi {
200+
idx: *idx,
201+
state,
202+
force_draw,
203+
now,
204+
})
205+
} else {
206+
state_guard.mark_delayed(*idx); // needs to be drawn later
207+
None
208+
}
179209
}
180210
TargetKind::TermLike {
181211
inner,
@@ -412,6 +442,20 @@ impl RateLimiter {
412442
}
413443
}
414444

445+
/// Check ahead if a call to `allow` will return `true`.
446+
fn will_allow(&self, now: Instant) -> bool {
447+
if now < self.prev {
448+
return false;
449+
}
450+
451+
let elapsed = now - self.prev;
452+
453+
// If `capacity` is 0 and not enough time (`self.interval` ms) has passed since
454+
// `self.prev` to add new capacity, return `false`. The goal of this method is to
455+
// make this decision as efficient as possible.
456+
self.capacity != 0 || elapsed >= Duration::from_millis(self.interval as u64)
457+
}
458+
415459
fn allow(&mut self, now: Instant) -> bool {
416460
if now < self.prev {
417461
return false;

src/multi.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::fmt::{Debug, Formatter};
22
use std::io;
3-
use std::sync::{Arc, RwLock};
3+
use std::sync::{Arc, RwLock, RwLockWriteGuard};
44
use std::thread::panicking;
55
#[cfg(not(target_arch = "wasm32"))]
66
use std::time::Instant;
@@ -9,6 +9,7 @@ use crate::draw_target::{
99
visual_line_count, DrawState, DrawStateWrapper, LineAdjust, ProgressDrawTarget, VisualLines,
1010
};
1111
use crate::progress_bar::ProgressBar;
12+
use crate::WeakProgressBar;
1213
#[cfg(target_arch = "wasm32")]
1314
use instant::Instant;
1415

@@ -263,6 +264,52 @@ impl MultiState {
263264
self.remove_idx(index);
264265
}
265266

267+
/// Mark that a given member has had its state changed without a redraw
268+
pub(crate) fn mark_delayed(&mut self, idx: usize) {
269+
self.members[idx].is_delayed = true;
270+
}
271+
272+
/// Mark that a given member does not need to be redrawn
273+
pub(crate) fn unmark_delayed(&mut self, idx: usize) {
274+
self.members[idx].is_delayed = false;
275+
}
276+
277+
/// Force the redraw of all delayed members. The input lock guard will be
278+
/// given back if and only if there was no member that had to be redrawn.
279+
pub(crate) fn force_redraw_delayed(
280+
mut this: RwLockWriteGuard<'_, Self>,
281+
now: Instant,
282+
) -> io::Result<Option<RwLockWriteGuard<'_, Self>>> {
283+
let to_redraw: Vec<_> = this
284+
.members
285+
.iter_mut()
286+
.filter(|mb| mb.is_delayed)
287+
.filter_map(|mb| {
288+
mb.is_delayed = false;
289+
mb.pb_weak.upgrade()
290+
})
291+
.collect();
292+
293+
if to_redraw.is_empty() {
294+
return Ok(Some(this));
295+
}
296+
297+
// The members will need to borrow this multibar to redraw!
298+
drop(this);
299+
300+
for pb in to_redraw {
301+
pb.state().draw(true, now)?;
302+
}
303+
304+
Ok(None)
305+
}
306+
307+
/// Check if the draw target will allow to draw. If not, the member will
308+
/// have to be marked as delayed to redraw it later.
309+
pub(crate) fn will_allow_draw(&self, now: Instant) -> bool {
310+
self.draw_target.will_allow_draw(now)
311+
}
312+
266313
pub(crate) fn draw(
267314
&mut self,
268315
mut force_draw: bool,
@@ -477,6 +524,10 @@ struct MultiStateMember {
477524
draw_state: Option<DrawState>,
478525
/// Whether the corresponding progress bar (more precisely, `BarState`) has been dropped.
479526
is_zombie: bool,
527+
/// Mark the member if he has not been redrawn after its last state change
528+
is_delayed: bool,
529+
/// Used to trigger a redraw if this member has been delayed
530+
pb_weak: WeakProgressBar,
480531
}
481532

482533
impl Debug for MultiStateMember {

0 commit comments

Comments
 (0)