User Hostile Dialog Box


Table of Contents

Git Git Git Git Git Git Git

What do I want to achieve

It’s important to set some target before jumping into and starting to do the thing. I would like to make an annoying “Donate Now” dialog

  • The dialog would show up whenever the user starts libreoffice, or whenever he switches to different modules like calc or writer etc.

  • The dialog would look something like the mockup shown below. The dialog should have a faint background image which shows all the community members, to give out cheerfull vibes. Other than that it would have a text which would be some interesting fact about libreoffice.

  • It would have an image on the top, and a “donate now” and a “close” button on the bottom It would also have a checkbox saying “I am broke”, which would be unchecked by default. If the user checks it, then the dialog will stop asking for monitory donations, and instead pester the user with “Other Ways To Contribute”, which would include all the getting involved stuff.

  • Other than that It would also have an “I don’t care” checkbox, which will be unchecked by default, and if checked, the dialog won’t show up again, and then one has to reactivate it from about > user-hostile-donation-dialog.

Git

This would makeup for a nice and fun learning experience. It would involve playing around with the UI, listeners, strings, “the user centiments” etc.

It’s not a rigid outline, but rather an idea to atleast get started. Patch on gerrit.

Starting with the UI

I will start with understanding how the about dialog is implemented, how does it display images and custom text etc. One thing that I noticed previously is that the “Tip Of The Day” dialog uses GtkDrawingArea while the about dialog uses GtkImage.

I asked on the #gtk IRC channel, and found that GtkImage is an image loaded from the disk at a specific size, while GtkDrawingArea is a canvas you draw on using cario.

This might have been the reason for why Libreoffice was carshing when I used GtkImage while trying to recreate the “Tip Of The Day” dialog. There I just created a bare constructor, without specifying the image.

Well for the user hostile dialog, GtkImage would work fine as I don’t need to change the image on the go.

Creating a Dialog

I created a dialog box using glade. The challenge that I faced was that I was not able to move the checkboxes in the ButtonBox of the GtkDialog to the left side. All 4 controls were stuck together. Then I looked into how the tip-of-the-day dialog does it, and there I found that whatever control I wanted to left align (in the button box), I had to turn on the packing > secondary control. What that is, I am still to investigate into.I got this Documentation Reference from the #gtk IRC channel.

For some reason, the dialog doesn’t carsh now :). Other issue that I faced was related to text wrapping. So if I set some long text in a weld::Label using the set_label(OUString) function, then it just expands the dialog to fit in one line. GtkImage is just a bitmap image, for which we specify the size (width) while loading it. For label wrapping, I will look into how the tip-of-the-day dialog does it. Making the dialog resizable doesn’t work as well. Under general > formatting of the GtkLabel there is an option to specify wrapping properties, and just below that are boxes for max width. Playing around with those, I was able to get text wrapping to work.

Sometimes these things feel hard, and I tend to avoid them as much as I can. But Now I realize that it is those hard moments which build me to tackel bigger challenges, and I should be looking for such opportunities. Today I find glade easier to use compared to yesterday

I got the dialog as I wanted. Looks nice! When I was working on the dialog, I changed the SID_ABOUT: unocommand to display the user-hostile-dialog instead of the about dialog. This way I was able to test whether it works as expected or not.

Git

Then I went ahead to work on the listeners and the configuration logic. There should be some way for us to remember what options were checked/unchecked when the dialog was closed (configuration), and some way know which module we are entering, so that we can change the text accordingly (listeners).

What Next?

I figured that for each dialog, we have a generic wrapper class called VclAbstractDialog. It provides “one type for all dialogs” solution, which makes sense. I followed the about/tip-of-the-day dialog to write all these functions and classes. And since I want a menu entry as well, I had to create an unocommand as well. I found that each module, and the start center etc have their own menubar.xml file. I added the user-hostile-dialog entry to most of those.

Adding Listeners

Looking into tip-of-the-day dialog, I found that when it is created, it adds a listener on the parent window, and checks if it is dying or not, and if so then it terminates as well. Then in the destructor, it removes the listener.

For adding listeners to show the dialog on such and such events, I had to read through the SfxViewFrame::Notify function, as described in the Blog. It listens for changes like document created/opened and creates the dialog then.

Remembering Dialog Configuration

The last part was to remember whether the user checked some checkbox or not, and remember that throughout the session, and between new sessions. I defined configuration defaults in xml file officecfg/registry/schema/org/openoffice/Office/Common.xcs and used the path to define a struct, through which we access/set the values, similar to how tip-of-the-day dialog does it.

struct UserHostileDialogDontCare : public comphelper::ConfigurationProperty<LastTipOfTheDayShown, sal_Int32> {
    static OUString path() { static constexpr OUString PATH(u"/org.openoffice.Office.Common/Misc/UserHostileDialogDontCare"_ustr); return PATH; }
private:
    UserHostileDialogDontCare(); // not defined
    ~UserHostileDialogDontCare(); // not defined
};

...

Now when the dialog is created , these configuration values are checked, and are used to set the state of the controls. When the dialog is destroyed, these choices are not lost. I forgot to set the type to boolean in the xml file, and as a result, the dialog was not showing up, so that’s something to reember as well, while copy pasting ;).

I added links to the checkboxes as well to change the Donate button to Contribute, if the user says “I’m broke!”

Button Opens Donation/Contribution page

Since the user can either donate or contribute, I had to redirect him to the correct page. For that I looked at how .uno:Donation does it. At first I tried to do it how the .uno:Donation does it, but then I thought why don’t I just call the uno commands!

So when the button is clicked, it dispatches uno commands depending on whether “[ ] I’m broke!” is checked or not.

What’s Left?

Since I want it to be User Hostile, there should be some way to show it when start center is opened. Other than that there should be some way to tell which module is opened, and change the label according to that.