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
.
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.
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.