Thursday, August 2, 2012

A Simple Explanation

(this is a post in progress and I might still edit it)

I will now take a small break on programming and design, and write about an unrelated subject. You do remember I warned in the beginning of this blog that I might do so, right?

Anyway, the inspiration for the words below came from Bilal Akhtar, specifically from his rant about Indian call center jokes and my subsequent response. For the record, I don't have any illusion that such words are particularly novel or clever. They just represent a realization that I needed to get off my chest and share because I'm surprised at how long it took me to have it.

Let me start with a little anecdote.

Some months ago, I attended the wedding of a high-school friend. The reception had no reserved tables save for the bride and groom, so me and a couple of friends sat randomly and inevitably ended up facing some people we never met before. A little smalltalk ensured, but before any basic question like "what do you do" or "where are you from" arose, one of such people pointed the finger at me laughing and said "Hey! I know you! You are the guy who just bought a plane, right?"

I was a little confused at first, but then realized: it was a reference to the fact that Hong Kong actor Jackie Chan had recently bought a jet. Since the jet was made by Brazilian manufacturer Embraer, those were prominent news around here. The guy was pretending to confuse me with Jackie Chan because I'm also of Asian heritage.

Yeah, I know. Fucking hilarious. Of course some details didn't really add up, such as:
  • I have long hair while Jackie Chan has short hair;
  • I sport a goatee while Jackie Chan does not;
  • Jackie Chan is 30 years older than me;
  • My stature and build are completely different;
but that didn't matter because we both look Asian and therefore look the same, right?

Upon hearing that, my friends and I left the table without saying much and went for some fresh air outside. The other group took the hint and, by the time we returned, they had already squeezed themselves into another table to avoid awkwardness. The rest of the night went smoothly, but that shitty moment inevitably became the most prominent memory of the entire wedding.

When discussing the incident, the friend who was there with me didn't want to raise the race card. He believed the guy was just an idiot rather than a racist idiot. I disagreed, but at the same time a lot of things were going through my head. Were we being over-sensitive? Had I better ways of handling the situation? Was my mood being influenced by other bad things that were happening in my life back then? However, even if I couldn't properly define or judge what I was feeling, I could say for sure I wasn't feeling it for the first time.

And it took me until yesterday to realize that last part was very important.

When discussing about a controversial joke, both sides often get lost in defining and judging what is offensive and what is not. This is usually a long and exhausting discussion, because it is inherently subjective and boils down to saying "my line is more accurately drawn than yours" while having no idea of what "accurate" means. On one hand, not many people on the politically correct side would go to the extreme of forbidding the usage of "insane" as a slang because it's potentially offensive to the mentally ill. On the other hand, many of the politically incorrect people tend to respect, for example, the Godwin limit. Why? You can argue all you want around it, but it's pretty much impossible to run away from subjectiveness.

However, I came to realize that the vast majority of both sides probably agrees that not many, if any, jokes stay funny when told multiple times. And therein lies a simple, easily understandable source of nuisance. When faced with a stereotype joke, a person actually hears two things: a misconception that might or might not be an offense, depending on which subjective definition you want to use, and a trivial variation of a gag heard many times before. The first tends to be a source of controversy, but how many people would disagree that the second is annoying?

This might sound trivial, but for me it's a very valuable realization because it sounds like an easy way to explain why you didn't like the stereotype joke without going through a tiresome discussion about the definition of offensiveness that will get you nowhere. Don't get me wrong: I want to make the world a better place and I would prefer if people avoided such jokes to begin with, but sometimes I just want people to shut the fuck up and starting a discussion tends to be a terrible way of accomplishing that.

So there you have it. A simple explanation. It's not a complete explanation and a discussion on whether a joke is not funny even on the first time is worth having, but this is an explanation anyone can easily understand: I don't want you to stop making stereotype jokes because of an arguable, subjective notion of offensiveness. I want you to stop doing them because they were less funny the second time, not funny the third time, and annoying the hundredth. I want you to stop because nothing ends up well when done excessively. And, most of all, I want you to stop because I want to attend weddings and have fond memories of them instead of shitty ones.

Saturday, February 11, 2012

Design: rethinking the style preferences

Back in the Acessibility: Contrast, colors, and fonts post, I explained how the color/style preferences changed depending on "contrast mode" being on or off:



Following a suggestion by Matthew Paul Thomas, I now merged the two versions into a single one, using a GtkComboBox.


This new version allows both modes to share all possible options. It also reduces interface complexity by eliminating the need for checkboxes in non-contrast mode.

I'm still wondering whether I will make a list of suggested colors available. I have no idea what they would be. :)

Tuesday, January 24, 2012

Coding: Aligning Widgets with GtkCheckButton Labels

As I mentioned in the previous post, I think I found a way to accurately align widgets with GtkCheckButton labels, though I have no idea if it's the easier way.


Basically, these are the GtkCheckButton style properties that define the left distance:


So all you need to do is set the margin-left of child widgets to:

focus-line-width + focus-padding + indicator-size + 3 * indicator-spacing

If you want to adapt immediately when the GTK theme changes, then you also need to connect to the style-updated signal appropriately. Here is a custom widget that gets the job done.

from gi.repository import GObject, Gtk

class CheckGroup(Gtk.VBox):
    def _on_checkbutton_style_updated(self, widget):
        value = GObject.Value()
        value.init(GObject.TYPE_INT)

        widget.style_get_property('focus-line-width', value)
        margin = value.get_int()

        widget.style_get_property('focus-padding', value)
        margin += value.get_int()

        widget.style_get_property('indicator-size', value)
        margin += value.get_int()

        widget.style_get_property('indicator-spacing', value)
        margin += 3 * value.get_int()

        self.group.set_margin_left(margin)

    def __init__(self, checkbutton):
        checkbutton.connect('style-updated', self._on_checkbutton_style_updated)

        self.group = Gtk.VBox(homogeneous=False, spacing=6)
        self.group.show()

        super(CheckGroup, self).__init__(homogeneous=False, spacing=6)
        self.pack_start(checkbutton, False, False, 0)
        self.pack_start(self.group, False, False, 0)
        self.show()

Monday, January 23, 2012

Design: To Label or Not To Label?

So, after Lars pointed me to the right direction, I think I managed to perfectly align widgets with checkbox labels. The next blog post will give the full details, but before that I have a small design question... Is it just me or the group labels are now visually redundant?

Compare


with


What do you think? Should I eliminate the labels or keep them for consistency with other windows?

By the way, I am aware that the entire authentication group should technically be inside the general group, but I couldn't figure out how to do this without making the layout look very unbalanced. Specially now that I'm aligning the widgets with the checkbox labels. Suggestions are welcome.

Friday, January 20, 2012

Design: Dialogs and error messages (2)

Following feedback from our favorite crazily-smiling designer and that great orange psychopath artist (I'm assuming the avatar is a self-portrait), here's the current state of the proxy dialog.



The italics were a personal touch.

Thursday, January 19, 2012

Design: Dialogs and error messages

Following up a comment by Matthew Paul Thomas on the Design: Dialogs, long operations, and error messages post, I'm experimenting with ways to show the error message in dialogs directly, without the need of hover or keyboard focus.

The hard part is inserting an error message inside the dialog layout in a way that's not aesthetically horrible. In particular, catering to the alignment issues of both short and long messages is complicated.

For now, I'm thinking about placing the message on the top.



This looks intrusive, as it moves the entire content downwards, but keep in mind that the dialog becomes insensitive during a long operation that can fail. So the user will not be trying to click anything in the moment the error appears.

What do you think? Suggestions?

Sunday, January 8, 2012

Heads Up: Drag 'n' Drop bug with PyGObject 3.0.0 (Ubuntu 11.10)

The GTK handling of drag 'n' drop looks a little bit convoluted at first, but can be quite handy once you get the hang of it. Unfortunately, there is a bug in PyGObject versions below 3.0.3 that makes things a little bit harder than they should.

Long story short, when using PyGObject in Ubuntu 11.10 Oneiric Ocelot, you can't pass detailed parameters to widget.drag_dest_set and expect GTK to take care of everything. You need to handle the drag-motion, drag-leave, and drag-drop signals yourself.

Acessibility: Contrast, colors, and fonts

As part of the goals for the current rewritings and refactorings of Polly, I'm trying to neglect accessibility a little less than I did until now. Because GTK offers great a11y support by default, this basically means taking some extra care when I edit widgets on my own instead of relying on the theme. One such care involves playing well with high-contrast themes.

Instead of a one-size-fits-all design, that would inevitably lead to some lowest common denominator aesthetic choices, the next versions of Polly will allow a --contrast command-line parameter that causes some subtle changes in the interface.

One of those changes involves the color of the error bar. In normal mode, it simply has our good old universal error color, red.


In contrast mode, the red is replaced by a color taken directly from the theme, ensuring readability while still drawing attention. Below you can see what how contrast mode looks when using the HighContrast and HighContrastInverse themes.


The other change I have implemented involves a new feature: I am going to introduce optional background color coding for unread items and mentions.


In contrast mode, where the choices of color are much more limited, I offer an alternative: marking unread items with bold and marking mentions with italic. This wasn't a very hard choice, since bold is a very common way of showing unread items in mail clients.

So when contrast mode is enabled, this is how the preferences window looks like.


What do you think? I'm not an accessibility expert, so I would love some feedback.

Heads Up: Missing Pango text scale constants in PyGObject 3.0 (Ubuntu 11.10)

If you are using PyGI in Ubuntu 11.10 Oneiric Ocelot and desperately trying to find the introspected versions of the Pango text scale constants, don't bother. I have confirmed on the development IRC channel that those constants are not introspected yet.

Since those are all #defines, I suggest simply using the values directly until this is fixed upstream. It's not like those will change any time soon...

Heads Up: Sensitivity bug in GTK 3.2 (Ubuntu 11.10)

The GTK 3.2 package in Ubuntu 11.10 Oneiric Ocelot has a small bug that I confirmed on the GTK IRC channel: if you set the "sensitive" property of a widget to true, the widget will become sensitive even when its parent is not. So weird things like this can happen.


In the image above, the password entry has the "sensitive" property set to true because the SOCKS5 protocol allows passwords. However, "send credentials to proxy server" is not checked, so the entire GtkHBox that contains both the username and password entries is not sensitive. Notice how the password entry is sensitive anyway.

A workaround for this bug is "reminding" GTK that the parent is insensitive when the child becomes sensitive. So instead of just

    password_entry.set_sensitive(True)

you have something like

    password_entry.set_sensitive(True)

    if not authentication_box.get_sensitive():
        authentication_box.set_sensitive(True)
        authentication_box.set_sensitive(False)

and when the upstream bug is fixed you can simply remove the extra lines.

Design: Dialogs, long operations, and error messages

Polly has a lot of operations that can take a long time to complete or can go wrong, so it's important to fit throbbers and messages somewhere in the interface. Since some of those operations happen in dialogs, I wanted a consistent way of handling those cases.

After some experiments, I settled for using the lower-left corner. The images below show the three states a dialog can have. Whenever a potentially long operation starts, a spinner is shown. If everything goes well, the spinner simply disappears and the window returns to normal. Otherwise, a stock error icon is shown.


The icon tooltip contains the error message, as seen below. For accessibility, the icon is also keyboard-focusable so the mouse is not needed to pop the tooltip.


The only thing I didn't like about my original implementation of this approach was that, for error -> throbber -> error transitions, sometimes the operation was so fast that the throbber wasn't shown at all, leaving the impression that nothing happened. See the video below.


So I added a small delay of 100ms before making the error icon appear. This delay is not large enough to annoy the user into thinking the operation is taking longer than necessary, but it's large enough to keep the visual changes below the flicker fusion threshold in practice.


What do you think? Suggestions?

Saturday, January 7, 2012

Coding: Python proxy support, thread-safety, and SocksiPy

Depending on the task, HTTP requests in Polly can be made by four different modules: pycurl, python-httplib2, python-oauth2, and urllib. The first has built-in proxy support and to the others I add proxy support with a little bit of monkey-patching: I replace the standard socket.socket class by a proxy-aware subclass from the SocksiPy module.

This is massively thread-unsafe, of course, because there are different threads creating instances of socket.socket all the time. This issue is present in the current version of Polly because I admittedly implemented proxy support in a little bit of a rush. I decided to get rid of it now, during the rewritings and refactorings I'm doing for the GTK3 port.

Now all HTTP requests are wrapped by methods of a proxy singleton, responsible for ensuring thread-safety. This is done via a reader/writer lock instead of a simple lock to allow multiple requests simultaneously. I also use this wrapping to replace the plethora of different exceptions given by the different modules by simpler Polly exceptions.

At least for now, I'm happy with this approach. One of the things I like about it is that the syntax makes sense. Now instead of things like

    http_client = httplib2.Http()
    http_client.request(url)

I have things like

    proxy.http_request(url)

which makes syntactically clear that the request is being handled, and possibly rerouted, by a proxy manager.

Thoughts, everyone? Simpler alternatives?

Friday, January 6, 2012

Coding: Python, GNOME-Keyring, and GObject Introspection


Nowadays, the fragmentation of the free desktop is a much smaller issue for developers than it was some time ago. With specifications like XDG Base Directory and efforts like QGtkStyle, creating an application that does not behave or look alien in most big players like GNOME and KDE is relatively simple.

But when developing Polly, I found out that there was one field where fragmentation is still a major problem: storage of sensitive information. GNOME has GNOME-Keyring, KDE has KWallet, and those two don't speak the same language at all. Luckily, I came across a lovely library called python-keyring that takes care of all the dirty work by automatically detecting the environment and choosing a proper keyring. I even talked about it in my Ubuntu App Developer Week session

This approach is not without its problems, though. The library falls back to its own encrypting implementation when it does not detect any supported keyring, and requires the user to type an unlocking password via terminal in this case. This is obviously an usability fail for any user that does not use GNOME or KDE.

Furthermore, the library depends on the static Python bindings to libgnome-keyring, which in turn depend on the static bindings to GObject. This conflicts directly with my current work of porting Polly to GTK3 and therefore PyGI, because static and introspected bindings cannot be used together. Some googling revealed that the developers were aware of the issue but could not do much because libgnome-keyring is hard to introspect.

I really didn't want to postpone the GTK3 porting by another cycle, so my frustration grew to a point where I actually asked Matthew Paul Thomas whether requiring to type a password on every start would be a good idea. My idea was basically writing a graphical interface for the python-keyring fallback. His answer was no. Among other followers, it was more diverse: it varied between no, no, and no. Despite its overwhelming popularity, I decided to ditch the idea. After Lars offered me support on IRC, I started to consider the risky route of trying to introspect libgnome-keyring.

There was light at the end of the tunnel, though: for a long time now, GNOME-Keyring and KWallet developers have been drafting a DBus-based Secret Service specification. I didn't pay much attention to it first because it isn't final, but then I learned that the GNOME-Keyring daemon is already being backed by it. This was perfect: I could use DBus directly, bypassing the need for libgnome-keyring bindings.

Only one problem, though: I used to rely on dbus-python for DBus work, and this library had too the problem of depending on deprecated bindings. So I realized I had to face the introspected GDBus bindings and cringed at the non-pythonic syntax that was expecting me.

And then, after some more googling... a wild Martin Pitt appeared! Thanks to Martin, my GDBus experience in Python has been much more pleasant than I expected. That was really the last piece of the puzzle. Now I could access GNOME-Keyring via the DBus Secret Service API elegantly, and without ever leaving GObject Introspection.

I still have to support KWallet for a while, until KDE's migration to KSecretService is completed. But since the Qt4/KDE4 bindings do not cause any conflicts, this is no biggie. The main code is now as simple as

    if FDOKeyring.supported():
        keyring = FDOKeyring(UID)
    elif KDEKeyring.supported():
        keyring = KDEKeyring(UID)
    else:
        exit('No supported keyring seems to be accessible.')


Followed by occasional calls of

    keyring.get_password(username) 
    keyring.set_password(username, password, callback)

and so on. What was particularly nice about this whole experience was not only how much I learned, but also the warm and fuzzy feeling that I'm really standing on the shoulders of a lot of giants. Kudos to everyone involved in the libraries.

Thursday, January 5, 2012

Here it goes...

So, after a lifetime of procrastination, I'm finally starting a blog.

For now, my primary intent is sharing some programming and design topics that I'm learning while developing Polly. But random thoughts on other subjects might occasionally appear.

Hello World!

The obligatory first post.