Commit 1cc47857 authored by Eisha Chen-yen-su's avatar Eisha Chen-yen-su

MediaViewer: Implement media viewer zoom

This implements the ability to use the zoom in the media viewer.
The user can zoom in and zoom out with the buttons around the zoom
entry and they can also enter an arbitrary zoom percentage.

See https://gitlab.gnome.org/World/fractal/issues/13
parent 17b5b388
......@@ -2,17 +2,39 @@ extern crate gtk;
use self::gtk::prelude::*;
use appop::AppState;
use app::App;
impl App {
pub fn connect_media_viewer_headerbar(&self) {
let op = self.op.clone();
let btn = self.ui.builder
let zoom_entry = self.ui.builder
.get_object::<gtk::Entry>("zoom_entry")
.expect("Cant find zoom_entry in ui file.");
zoom_entry.connect_activate(move |_| {
op.lock().unwrap().change_zoom_level();
});
let op = self.op.clone();
let zoom_out_button = self.ui.builder
.get_object::<gtk::Button>("zoom_out_button")
.expect("Cant find zoom_out_button in ui file.");
zoom_out_button.connect_clicked(move |_| {
op.lock().unwrap().zoom_out();
});
let op = self.op.clone();
let zoom_in_button = self.ui.builder
.get_object::<gtk::Button>("zoom_in_button")
.expect("Cant find zoom_in_button in ui file.");
zoom_in_button.connect_clicked(move |_| {
op.lock().unwrap().zoom_in();
});
let op = self.op.clone();
let back_btn = self.ui.builder
.get_object::<gtk::Button>("media_viewer_back_button")
.expect("Cant find media_viewer_back_button in ui file.");
btn.connect_clicked(move |_| {
back_btn.connect_clicked(move |_| {
op.lock().unwrap().hide_media_viewer();
});
}
......
......@@ -2,6 +2,7 @@ extern crate gtk;
use self::gtk::prelude::*;
use uibuilder;
use appop::AppOp;
use appop::AppState;
......@@ -9,13 +10,17 @@ use widgets::image;
use types::Room;
#[derive(Clone)]
pub struct MediaViewer {
media_urls: Vec<String>,
current_url_index: usize,
image: image::Image,
zoom_levels: Vec<f64>,
}
impl MediaViewer {
pub fn from_room(room: &Room, current_media_url: &str) -> MediaViewer {
pub fn new(room: &Room, current_media_url: &str, image: image::Image) -> MediaViewer {
let img_msgs = room.messages.iter().filter(|msg| msg.mtype == "m.image");
let media_urls: Vec<String> = img_msgs.map(|msg| msg.url.clone().unwrap_or_default()).collect();
......@@ -24,15 +29,21 @@ impl MediaViewer {
MediaViewer {
media_urls,
current_url_index,
image,
zoom_levels: vec![0.025, 0.05, 0.1, 0.25, 0.5, 0.75, 1.0],
}
}
pub fn set_zoom_level(&self, zlvl: f64) {
*self.image.zoom_level.lock().unwrap() = Some(zlvl);
self.image.widget.queue_draw();
}
}
impl AppOp {
pub fn display_media_viewer(&mut self, url: String, room_id: String) {
let rooms = self.rooms.clone();
let r = rooms.get(&room_id).unwrap();
self.media_viewer = Some(MediaViewer::from_room(r, &url));
self.set_state(AppState::MediaViewer);
......@@ -51,6 +62,18 @@ impl AppOp {
media_viewport.add(&image.widget);
media_viewport.show_all();
self.media_viewer = Some(MediaViewer::new(r, &url, image.clone()));
let ui = self.ui.clone();
let zoom_level = image.zoom_level.clone();
image.widget.connect_draw(move |_, _| {
if let Some(zlvl) = *zoom_level.lock().unwrap() {
update_zoom_entry(&ui, zlvl);
}
Inhibit(false)
});
self.set_nav_btn_sensitivity();
}
......@@ -94,6 +117,18 @@ impl AppOp {
image.widget.show();
media_viewport.add(&image.widget);
let ui = self.ui.clone();
let zoom_level = image.zoom_level.clone();
image.widget.connect_draw(move |_, _| {
if let Some(zlvl) = *zoom_level.lock().unwrap() {
update_zoom_entry(&ui, zlvl);
}
Inhibit(false)
});
mv.image = image;
}
self.set_nav_btn_sensitivity();
......@@ -126,6 +161,18 @@ impl AppOp {
image.widget.show();
media_viewport.add(&image.widget);
let ui = self.ui.clone();
let zoom_level = image.zoom_level.clone();
image.widget.connect_draw(move |_, _| {
if let Some(zlvl) = *zoom_level.lock().unwrap() {
update_zoom_entry(&ui, zlvl);
}
Inhibit(false)
});
mv.image = image;
}
self.set_nav_btn_sensitivity();
......@@ -154,4 +201,56 @@ impl AppOp {
}
}
}
pub fn zoom_out(&self) {
if let Some(ref mv) = self.media_viewer {
let zoom_level = *mv.image.zoom_level.lock().unwrap();
if zoom_level.is_none() ||
zoom_level.unwrap() <= mv.zoom_levels[0] {
return;
}
if let Some(new_zlvl) = mv.zoom_levels.iter()
.filter(|zlvl| **zlvl < zoom_level.unwrap()).last() {
mv.set_zoom_level(*new_zlvl);
}
}
}
pub fn zoom_in(&self) {
if let Some(ref mv) = self.media_viewer {
let zoom_level = *mv.image.zoom_level.lock().unwrap();
if zoom_level.is_none() ||
zoom_level.unwrap() >= mv.zoom_levels[mv.zoom_levels.len() - 1] {
return;
}
if let Some(new_zlvl) = mv.zoom_levels.iter()
.filter(|zlvl| **zlvl > zoom_level.unwrap()).nth(0) {
mv.set_zoom_level(*new_zlvl);
}
}
}
pub fn change_zoom_level(&self) {
if let Some(ref mv) = self.media_viewer {
let zoom_entry = self.ui.builder
.get_object::<gtk::EntryBuffer>("zoom_level")
.expect("Cant find zoom_level in ui file.");
match zoom_entry.get_text().trim().trim_right_matches('%').parse::<f64>() {
Ok(zlvl) => mv.set_zoom_level(zlvl / 100.0),
Err(_) => if let Some(zlvl) = *mv.image.zoom_level.lock().unwrap() {
update_zoom_entry(&self.ui, zlvl)
},
}
}
}
}
fn update_zoom_entry(ui: &uibuilder::UI, zoom_level: f64) {
let zoom_entry = ui.builder
.get_object::<gtk::EntryBuffer>("zoom_level")
.expect("Cant find zoom_level in ui file.");
zoom_entry.set_text(&format!("{:3.0}%", zoom_level * 100.0));
}
......@@ -33,6 +33,7 @@ pub struct Image {
pub pixbuf: Arc<Mutex<Option<Pixbuf>>>,
/// useful to avoid the scale_simple call on every draw
pub scaled: Arc<Mutex<Option<Pixbuf>>>,
pub zoom_level: Arc<Mutex<Option<f64>>>,
pub thumb: bool,
pub circle: bool,
pub fixed_size: bool,
......@@ -74,6 +75,7 @@ impl Image {
widget: da,
pixbuf: Arc::new(Mutex::new(None)),
scaled: Arc::new(Mutex::new(None)),
zoom_level: Arc::new(Mutex::new(None)),
thumb: thumb,
circle: circle,
backend: backend.clone(),
......@@ -119,6 +121,7 @@ impl Image {
let max_size = self.max_size.clone();
let pix = self.pixbuf.clone();
let scaled = self.scaled.clone();
let zoom_level = self.zoom_level.clone();
let is_circle = self.circle.clone();
let fixed_size = self.fixed_size;
let centered = self.centered;
......@@ -151,7 +154,18 @@ impl Image {
}
if let Some(ref pb) = *pix.lock().unwrap() {
let (pw, ph) = adjust_to(pb.get_width(), pb.get_height(), rw, rh);
let (mut pw, mut ph) = adjust_to(pb.get_width(), pb.get_height(), rw, rh);
if let Ok(mut zoom_level_guard) = zoom_level.lock() {
match zoom_level_guard.clone() {
Some(zl) => {
pw = (pb.get_width() as f64 * zl) as i32;
ph = (pb.get_height() as f64 * zl) as i32;
},
None => *zoom_level_guard = Some(pw as f64 / pb.get_width() as f64),
}
}
if fixed_size {
da.set_size_request(pw, ph);
} else {
......
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