Introducing a media viewer for Fractal

Fractal is a Matrix client for GNOME and is written in Rust. Matrix is an open network for secure, decentralized communication.

These past two weeks, I have made a lot of progress on my GSoC project, I have:

I will talk more about the image viewer in this post.

The initial issue

The initial issue was about improving how images were handled within Fractal: Fractal was using the `xdg-open` command in order to ask you which application you would like to use to open an image. So it looked like this:


Further more, because the media are downloaded into the folder `~/.cache/fractal` under their hash name before calling `xdg-open`, there are two major problems:

  • The names given to the pictures have no sens at all (in “Select an application to open […]”)
  • If you use “Eye of GNOME” to open this image, you will end up navigating among all the images contained into the Fractal’s cache folder where there are avatars and thumbnails from the current and other rooms which is absolutely not what we would want to have

So many solutions has been mentioned over the past months, I had tried to implement a simple dialog that was showing the image with three simple action: display the image in its real size, make the image fit inside the dialog and download the image. My prototype can be seen on this branch.

Finally, Tobias made a design that has been discussed during the Hackfest:

The media viewer wouldn’t be a separate dialog but it would be integrated within the main application window, just like the room directory. There would be a zoom entry that would enable the user to scale the image up and down, a button to enter in full screen mode and an overflow menu that would show other operations such as downloading the image.

How I have implemented it

I first implemented the design of the media viewer with Glade and then integrated it within the two GtkStacks of the main window. There is a stack named `headerbar_stack` for the different header bars used by Fractal and a `main_content_stack` for the different window contents. The elements of the stacks to show are determined depending on the internal state of the application, so I created a new `MediaViewer` state that would show the header bar and the viewer that I implemented with Glade. When the user clicks on an image in a room, the program enters in this state and when they clicks on the back button of the media viewer, it goes back to the chat view. You can view these changes in this commit.

Next I implemented the ability to display the clicked image in the media viewer. I’ve used the Image custom widget but there was a big issue: the Imagewidget needed to be passed a maximum size for the GtkDrawingArea but (for an reason I couldn’t understand) the allocated size (width, height) of the GtkViewport in which the Image widget would be placed into was always of (1, 1), so it wasn’t usable to build the Image widget. So I had to make the size argument optional and then it worked fine.

Next I implemented the ability to navigate in the pictures available in a room. For this, I created a new MediaViewer struct that holds two field:

  • media_urls that is a Vec<String> that holds the URLs of the media (for now just the pictures) currently loaded in the room.
  • current_url_index that is a usize that allows us to know the position of the URL (within media_urls) of the media currently viewed.

Then in order to go to the previous/next media, we decrease/increase by one the value of current_url_index and then we load the image with the URL media_urls[current_url_index]. You can view the commit here.

The image was displayed but it wasn’t centered, it was at the top-left corner of the GtkViewport. For some reasons (probably related to the allocated size issue), I couldn’t use methods such as set_halign in order to center the Imagewidget. So I had to implement a work around in which I would set up a custom origin when drawing the image on the GtkDrawingAreain order to have it centered.

Implementing the ability to zoom in and out on the image took me quite much time. I first implemented the ability to enter an arbitrary zoom level to view the image and then Implemented the behavior of the “zoom out” and “zoom in” buttons so that they would request predefined zoom levels. See this commit for more details about it.

The title bar of the media viewer must be changed according to the media’s file name. This was rather easy to implement: I just needed to add a field media_names (a Vec<String>)in theMediaViewerstruct, it was easy to fill as media names are store in the body field of the media’s message, finally we set the title bar’s text with media_names[current_media_index](the name of the index was changed from “current_url_index” to “current_media_index” in this commit). You can see the full details here.

Finally, I implemented the full screen mode of the viewer: when the user clicks on the full screen button in the header bar, it enters in the full screen mode and in order to exit this mode, the viewer can press escape. It has been implemented in this commit.

Here is the final result of the image viewer I’ve made:

Capture du 2018-06-15 16-15-41

What can still be improved

There are however some things that are yet to be improved:

  • The buttons to zoom in and out are always sensitive: one of them should be insensitive when we are at the maximum/minimum zoom level.
  • The zoom percentage input entry is a little bit messy.
  • The image is not resized when entering in full screen mode.
  • You cannot go further back (with the previous button) in the media history than the loaded messages of the current room.
  • You cannot zoom more than 100% of the image size
  • When the image is larger than the viewport, the horizontal scroll isn’t always available.

I will open an issue and solve some of these problems before moving to the next task.


One thought on “Introducing a media viewer for Fractal

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s