Commit e2abb53a authored by Daniel García Moreno's avatar Daniel García Moreno

image: Spinner when loading images async

See #250
parent dd848b46
Pipeline #13124 passed with stages
in 19 minutes and 3 seconds
...@@ -191,3 +191,15 @@ stack.titlebar:not(headerbar) > box > separator { ...@@ -191,3 +191,15 @@ stack.titlebar:not(headerbar) > box > separator {
.image-widget { .image-widget {
padding: 3px; padding: 3px;
} }
@keyframes spin {
to { -gtk-icon-transform: rotate(1turn); }
}
.image-spinner {
background-color: @insensitive_bg_color;
-gtk-icon-source: -gtk-icontheme('process-working-symbolic');
opacity: 1;
animation: spin 1s linear infinite;
}
...@@ -35,20 +35,13 @@ pub struct Image { ...@@ -35,20 +35,13 @@ pub struct Image {
impl Image { impl Image {
pub fn new(backend: &Sender<BKCommand>, path: &str, size: (i32, i32), Thumb(thumb): Thumb) -> Image { pub fn new(backend: &Sender<BKCommand>, path: &str, size: (i32, i32), Thumb(thumb): Thumb) -> Image {
let da = DrawingArea::new();
let pixbuf = match gtk::IconTheme::get_default() {
None => None,
Some(i1) => match i1.load_icon("image-loading-symbolic", size.1, gtk::IconLookupFlags::empty()) {
Err(_) => None,
Ok(i2) => i2,
}
};
let da = DrawingArea::new();
let img = Image { let img = Image {
path: path.to_string(), path: path.to_string(),
max_size: size, max_size: size,
widget: da, widget: da,
pixbuf: Arc::new(Mutex::new(pixbuf)), pixbuf: Arc::new(Mutex::new(None)),
scaled: Arc::new(Mutex::new(None)), scaled: Arc::new(Mutex::new(None)),
thumb: thumb, thumb: thumb,
backend: backend.clone(), backend: backend.clone(),
...@@ -73,7 +66,8 @@ impl Image { ...@@ -73,7 +66,8 @@ impl Image {
let h = pb.get_height(); let h = pb.get_height();
da.set_size_request(w, h); da.set_size_request(w, h);
} else { } else {
da.set_size_request(w, h); // No image yet, square image
da.set_size_request(h, h);
} }
let pix = self.pixbuf.clone(); let pix = self.pixbuf.clone();
...@@ -87,40 +81,17 @@ impl Image { ...@@ -87,40 +81,17 @@ impl Image {
// //
// This allow the user to resize to less than this image width dragging the window // This allow the user to resize to less than this image width dragging the window
// border, but it's slow because we're resizing 10px each time. // border, but it's slow because we're resizing 10px each time.
let mut rw = w; let rw = match parent_box_width(da) {
let mut parent: Option<gtk::Widget> = da.get_parent(); Some(pw) if pw - 10 < w => { pw - 10 },
loop { _ => { w },
if parent.is_none() { };
break;
}
let p = parent.unwrap();
if p.is::<gtk::Box>() {
let parent_width = p.get_allocated_width();
let max = parent_width - 10;
if max < w {
rw = max;
}
break;
}
parent = p.get_parent();
}
let context = da.get_style_context().unwrap(); let context = da.get_style_context().unwrap();
gtk::render_background(&context, g, 0.0, 0.0, width, height); gtk::render_background(&context, g, 0.0, 0.0, width, height);
if let Some(ref pb) = *pix.lock().unwrap() { if let Some(ref pb) = *pix.lock().unwrap() {
let mut pw = pb.get_width(); let (pw, ph) = adjust_to(pb.get_width(), pb.get_height(), rw, h);
let mut ph = pb.get_height();
if pw > ph && pw > rw {
ph = rw * ph / pw;
pw = rw;
} else if ph >= pw && ph > h {
pw = h * pw / ph;
ph = h;
}
da.set_size_request(pw, ph); da.set_size_request(pw, ph);
let mut scaled_pix: Option<Pixbuf> = None; let mut scaled_pix: Option<Pixbuf> = None;
...@@ -141,6 +112,8 @@ impl Image { ...@@ -141,6 +112,8 @@ impl Image {
g.fill(); g.fill();
*scaled.lock().unwrap() = Some(sc); *scaled.lock().unwrap() = Some(sc);
} }
} else {
gtk::render_activity(&context, g, 0.0, 0.0, height, height);
} }
Inhibit(false) Inhibit(false)
...@@ -161,11 +134,18 @@ impl Image { ...@@ -161,11 +134,18 @@ impl Image {
let pix = self.pixbuf.clone(); let pix = self.pixbuf.clone();
let scaled = self.scaled.clone(); let scaled = self.scaled.clone();
let da = self.widget.clone(); let da = self.widget.clone();
if let Some(style) = da.get_style_context() {
style.add_class("image-spinner");
}
gtk::timeout_add(50, move || match rx.try_recv() { gtk::timeout_add(50, move || match rx.try_recv() {
Err(TryRecvError::Empty) => gtk::Continue(true), Err(TryRecvError::Empty) => gtk::Continue(true),
Err(TryRecvError::Disconnected) => gtk::Continue(false), Err(TryRecvError::Disconnected) => gtk::Continue(false),
Ok(fname) => { Ok(fname) => {
load_pixbuf(pix.clone(), scaled.clone(), da.clone(), &fname); load_pixbuf(pix.clone(), scaled.clone(), da.clone(), &fname);
if let Some(style) = da.get_style_context() {
style.remove_class("image-spinner");
}
gtk::Continue(false) gtk::Continue(false)
} }
}); });
...@@ -224,3 +204,40 @@ pub fn is_gif(fname: &str) -> bool { ...@@ -224,3 +204,40 @@ pub fn is_gif(fname: &str) -> bool {
let result = tree_magic::from_filepath(p); let result = tree_magic::from_filepath(p);
result == "image/gif" result == "image/gif"
} }
fn parent_box_width<W: gtk::WidgetExt>(widget: &W) -> Option<i32> {
let mut parent: Option<gtk::Widget> = widget.get_parent();
let mut w: Option<i32> = None;
loop {
if parent.is_none() {
break;
}
let p = parent.unwrap();
if p.is::<gtk::Box>() {
let parent_width = p.get_allocated_width();
w = Some(parent_width);
break;
}
parent = p.get_parent();
}
w
}
/// Adjust the `w` x `h` to `maxw` x `maxh` keeping the Aspect ratio
fn adjust_to(w: i32, h: i32, maxw: i32, maxh: i32) -> (i32, i32) {
let mut pw = w;
let mut ph = h;
if pw > ph && pw > maxw {
ph = maxw * ph / pw;
pw = maxw;
} else if ph >= pw && ph > maxh {
pw = maxh * pw / ph;
ph = maxh;
}
(pw, ph)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment