Refactor: Backend and UI

Fractal is currently structured into two parts: The API part (fractal-matrix-api) and GTK part (fractal-gtk). The first one mostly just does the https calls to the Matrix server, the GTK part does everything else. This post will not talk about the API part since that will remain more or less the same (at least for now).

Why do this split?

Our goal is to create two separate Apps for two different use cases: Banquets and Barbecues. For this split to happen we first need to make some changes to Fractal’s architecture.

I think to keep Fractal maintainable in the future we need to simplify especially the AppOp struct, since it has been growing at an alarming pace (Don’t get me wrong I’m really happy that Fractal improves so quickly). AppOp is the struct which contains all widget references and most methods. Basically, it contains everything important.

Also, a lot of Matrix related code is intertwined with GTK code, and therefore many tasks are handled in the main thread.

New architecture

The communication between fractal-matrix-api and the Fractal backend will stay the same. Therefore, the backend-loop will live in the backend, but instead of making UI changes directly, it will just store the new data, and then signal the main GTK application that it has to update the related UI component. The backend will expose a set of methods to give the main application access to the data and to change them.

The main application will create all UI components and keep them up-to-date. At last we have the UI components which are “custom widgets”, such as the sidebar. A custom widgets has two public methods, one to create a new component, and another one to update its content.

When the main application receives a UI update event, containing only information about which UI component should be updated, but no other data, the application uses the interface offered by the backend to get all needed information to call the update method of each custom widget. The update method then decides then which GTK widgets have to be destroyed, and which can be reused to display the new data.

How to get started?

The first thing we can do is switch form Mutex to RwLock, this will already solve many UI freezes since we don’t need to look the data anymore to read it.

Then we can start moving the current code form AppOp, where possible, to a custom widget (the Rust structs we are using right now). Each widget should have a create and an update method, for creating the new component and for keeping it updated without the need of destroying it first. (Address.rs is a widget example created this way). Most custom widgets already have the create method called widget() (if I remember it correctly) but they are missing the update method.

Nothing is set in stone

We didn’t decide anything yet. I wrote this blog post to start the discussion about how to tackle the refactor and split, so input/comments/feedback are very welcome!

Fractal GSoC Progress

In the last two weeks I have been working on the user account settings for Fractal. I can’t wait to get to the point where Fractal will work stand-alone without needing to use Riot for certain things, and getting complete account settings is a major step in that direction.

Let’s start with the status of Fractal before I started my task. The only thing Fractal did was loading the display name and the avatar, and they couldn’t even be changed.

Design

Tobias had made mockups for the account settings last fall, but they did not contain all the necessary features and was never fully implemented. During the Strasbourg hackfest we started discussing a more complete design. Since then we’ve been expanding on that work, which led us to the current design:

Coding

I started creating the UI file which describes the view. I tried to focus just on the UI, in order to not overcomplicate my process. The first thing was just a dialog with all widgets without any behavior.
The second step was to add all information already available in Fractal, like homeserver, MXID, avatar, and display name.

Until this point the implementation was quite easy because it was mostly GTK code, which I’m quite familiar with.
However, up until two weeks ago I had never touched the fractal-api code, so the rest was trickier. Most APIs, e.g. setting the avatar, are quite easy to implement. The most complicated are the calls made for handling third party identifiers (https://matrix.org/docs/spec/client_server/r0.3.0.html#adding-account-administrative-contact-information) (email addresses and phone numbers). It is not only about setting the new identifier, but the client has also the request a token to make sure the user is the owner of the email address.

Current State

The user can now change the following settings: Avatar, display name, add and remove email addresses and phone numbers. Also, they can see the homeserver and their own MXID.
I already added all the API calls except for the calls needed for account destruction. Over the next few days I will finish up the last missing things.

Future

Tobias and I talked about moving the account settings from a dialog to the main window, and removing the apply button, since the process for adding emails addresses needs a separate conformation (because it requires that the user goes to their email inbox). Also, for changing the password the user would have to confirm their changes twice, which doesn’t make much sense. We will probably make some more small changes on the way to make the UX as good as we possibly can.

I’m working on issue #21 and this is my WIP branch.

Fractal Hackfest 2018

The last couple of days I was in Strasbourg for the Fractal Hackfest. We made some fundamental decisions for the future of Fractal, our Matrix client.  We also decided on some basic architectural changes we want to make.

You probably already read about the split of Fractal into two separate apps, to cover two different use cases: Barbecue and Banquet. Barbecue will mostly cover “one to one” chats and Banquet high-traffic group chats and IRC-like rooms. We are certain that the split is the right direction for Fractal, but we didn’t define the exact split between the apps.

Before we can start working on the two different apps we need to refactor the code. Currently Fractal is structured in two parts: fractal-api and fractal-gtk. The first one is a library which handles all the communication with Matrix. The other part contains all logic and UI. The first big task will be to nicely separate the logic from the UI, so that the code can be shared between the apps and have just few different UI elements, to better fit each use case. We talked about a lot of things and Daniel and Tobias have written some really good posts to sum up the event, therefore I won’t go into every detail in this post.

This picture is from a lunch break. We got our food from different restaurants and ate all together. This was my first hackfest, and I had a lot of fun, I definitely loved the city and made new friends.

Emoji Picker

During the train ride to Strasbourg I did some hacking. I worked on a patch for the gtk emoji chooser, which on many systems looks quite broken:

After my changes the popover will look like this:

I already opened a merge request to GTK. We are still discussing a few things but I hope it will be merged soon.

Spell check

I also worked on the integration of spell check into Fractal. I will use gspell, an integration for spellcheck for GTK applications, also used by Gedit. The first step was to create gspell Rust bindings, but I will write a different post on that and how to create Rust bindings with GIR. For now, here’s a basic demonstration of the working gspell-rs bindings:

Not just coding

I didn’t only code and work on Fractal, but I also a got to do a little bit of sightseeing. The last day (Monday) was completely different than the previous days, since everybody had to leave and we hadn’t planned any thing for it. I visited the cathedral, from inside and I also climbed the top to see the panorama of Strasbourg. For lunch we, the remaining people, ate at a local restaurant. On the menu was limitless Flamenkuchen, with close to 1 liter of beer for each of us. 

Google Summer of Code

Many people already know I got accepted as a student to GSoC. It was really nice to meet my mentor, Daniel and also Eisha, the other student working on Fractal. Now I can connect the Matrix ID to a real person, which makes interaction with them much easier online. I guess this Hackfest was a really nice conclusion for the community bonding period and a good start for GSoC.

The time in Strasbourg was limited therefore we didn’t have much time to define all of the tasks for the summer.  But I will be starting to work on the User Account Settings for this week. Also the spell checking is one of my proposed GSoC tasks, so I’m already ahead of my planned schedule. I definitely won’t have trouble finding more tasks though :)

Thank You!

Thanks to the GNOME Foundation and Purism for making it possible for me to attend this hackfest. GNOME sponsored my trip to and from Strasbourg, and Purism paid for my accomodation (I shared a room with Tobias). The location was awesome, and the city was perfect for a hackfest, I am so glad I have been there. I would also like to thank everyone who organized and helped make the event happen. I think it was a big success and this is one of the reasons I love the GNOME community so much.

Animating a ScrolledWindow

The other day I worked on improving the auto-scroll in Fractal (a super cool GTK+ Matrix Client). While doing this I discovered some nice features in GTK+.

Scrolling the view automatically (e.g. by pressing a button) is quite easy, you just need to set the value of vadjustment or hadjustment and then the view jumps to the new value.
This code moves the view to the bottom (yes, it’s Rust 😎):

if let Some(adj) = view.get_vadjustment() {
  adj.set_value(adj.get_upper() - adj.get_page_size());
}

View contains our ScrolledWindow and upper – page_size is the bottom of our view.

Awesome! We are done here!

But wait! What if we want to have a fancy animation, without the sudden jump? That’s exactly what we wanted in Fractal.

Let’s look at how we can add the smooth animation between the starting point and the end point.

The idea behind this is to update the position of the ScrolledWindow gradually instead of all at once. GTK provides us with a pulse to animate the state change, called tick. So we need to increase the value of the adjustment at each “tick”. A tick is fired every time GTK draws a new frame, so we don’t need to think about keeping in sync with the frame rate. For attaching a callback to the tick we use the function add_tick_callback(). We need also the frame clock, the start “tick”. We get the the clock with get_frame_clock(). This allows us to define the length of the animation, because the end of the transition will be start_clock + animation_duration. Now all we need is to calculate the position of the view at each “tick”.

This code snippet is inspired by the GTK+ source code. It moves a ScrolledWindow to the end of the view and could be adapted to scroll automatically (with an animation) to any point:

fn scroll_down(view: gtk::ScrolledWindow, animate: bool) {
    if let Some(adj) = view.get_vadjustment() {
        if animate {
            if let Some(clock) = view.get_frame_clock() {
                let duration = 200;
                let start = adj.get_value();
                let end = adj.get_upper() -
                          adj.get_page_size();
                let start_time = clock.get_frame_time();
                let end_time = start_time + 1000 * duration;
                view.add_tick_callback(move |_view, clock| {
                    let now = clock.get_frame_time();
                    if now < end_time &&
                       adj.get_value() != end {
                        let mut t = (now - start_time)
                                     as f64 /
                                     (end_time - start_time)
                                     as f64;
                        t = ease_out_cubic(t);
                        adj.set_value(start + 
                                      t * 
                                      (end - start));
                        return glib::Continue(true);
                    }
                    else
                    {
                        adj.set_value (end);
                        return glib::Continue(false);
                    }
                });
            }
        }
        else {
            adj.set_value(adj.get_upper() -
                          adj.get_page_size());
        }
    }
}

Instead of a linear animation I used a cubic easing function (the code was found inside the GTK+ source)

/* From clutter-easing.c, based on Robert Penner's
 * infamous easing equations, MIT license.
 */
fn ease_out_cubic (t: f64) -> f64 {
  let p = t - 1f64;
  return p * p * p + 1f64;
}

This code is from Fractal. The complete commit can be found here. I really hope that this post saves somebody from reading the GTK+ source code like I had to do, to figure out how to animate a simple ScrolledWindow :)

FOSDEM 2018

Last weekend I was in Brussels for FOSDEM, a super awesome conference about free and open source software. Since my first year, three years ago, a few things have changed. This year I went as a speaker and I brought with me a talk about my experience writing Teleport, my first GTK+ application. I really hope I could motivate somebody to start their own project. Also, in the past year my relationship to free software has changed, from just being a user and advocate to an active contributor (primarily to the GNOME Project).

I stayed in Brussels an entire week to save some money on the travel costs and visit Brussels. I ended up staying inside most of the time though, not only to escape the cold and rainy weather, but also because I get super motivated at conferences to work on projects. I mostly worked on the implementation of the redesign of the background settings panel for GNOME, and I really hope we can still get it into GNOME 3.28 (fingers crossed!). In order to build the background panel I had to use jhbuild for the first time, and I had to learn it quickly because I had to finish the patch. So I spent a lot of time getting jhbuild running, while already preparing the talk in my head and going to social dinners and beer events, which was a bit much at times ;)

Sorry that you had to wait, David and Tobias, to go to the Purism dinner (Picture by Tobias)

On Sunday I helped out at the GNOME booth. Luckily in the beginning there where not too many people, most likely they had a hard time getting up after the GNOME beer event the night before :D
Therefore I had time to get comfortable at the booth and to learn what I needed to do.

I had a nice time at the booth, not only with Bastian (Picture by Tobias)

As my talk got closer I got more and more nervous, but at least the previous days I was really calm about it. My talk was at 4 pm on Sunday in the Open Source Design devroom. It went well, except for a pretty bad case of the demo effect: The demo didn’t work on the first try, then my touchpad suddenly stopped working, and the entire time the projector was set at a resolution that cut off part of the slides :/

The beginning of my Teleport talk (Picture by Tobias)

The most important thing I learned this year at FOSDEM (tip for all FOSDEM attendees) was to buy a “MOBIB Basic” card for 5 Euros and charge it with “10 journeys JUMP”s. This is much cheaper than buying single journey tickets, and it can even be used by more than one person at the same time.

All in all I really loved the event. I made a lot of new experiences, met new people and had interesting conversations. I’m already excited to come back to Brussels next year!