Last week I shared the news that all large features had been implemented; all that was left were issues raised by GNOME contributors when my mentor demoed my progress over GUADEC. This week I’ve just been chugging away on those issues; let me show you.
Hadess noted that the save button wasn’t obvious when my mentor demoed Piper to him; I already mentioned this last week: the icon is a save to disk icon and it’s not obvious at first glance that you have to press it to write the changes you made to the device. I discussed that we want to go for a time-based commit, where the changes are committed to the device automatically after certain events (e.g. switching stack pages) or after a certain interval of inactivity. Since that will require more work and is thus something for a later version, I went with the other options to at least solve the problem for now:
As you can see, the icon has been replaced with text and the button is now
insensitive when there are no changes to be committed. When there are, the
button is sensitive and the suggested-action
CSS class is applied to the
button, in order to draw the user’s attention that something needs to be
committed. You can view the pull request
here.
Related to the above (and unsurprisingly also suggested by hadess) is asking for confirmation when the user attempts to close Piper with unsaved changes:
This shows the versatility of the perspective abstraction; all it takes is adding a single new property to each perspective:
def can_shutdown(self):
"""Whether this perspective can safely shutdown."""
for profile in self._device.profiles:
if profile.dirty:
return False
return True
This property signals whether a perspective can safely shutdown. On a delete event, the window checks all its perspectives' properties and if one perspective signals that it cannot safely shutdown, it presents the dialog:
def do_delete_event(self, event):
for perspective in self.stack_perspectives.get_children():
if not perspective.can_shutdown:
dialog = Gtk.MessageDialog(self, Gtk.DialogFlags.MODAL,
Gtk.MessageType.QUESTION,
Gtk.ButtonsType.YES_NO,
_("There are unapplied changes. Are you sure you want to quit?"))
response = dialog.run()
dialog.destroy()
if response == Gtk.ResponseType.NO or response == Gtk.ResponseType.DELETE_EVENT:
return Gdk.EVENT_STOP
return Gdk.EVENT_PROPAGATE
This was the last unimplemented feature that my mentor and I agreed Piper had to have when we came up with the GSoC proposal. I saved it for last because it’s quite a niche case to own (let alone use simultaneously) two devices, but it had to be done at some point. The welcome perspective was already implemented last week; it was simply a matter of adding the right signals to the ratbagd bindings, fixing said bindings, adding methods to add and remove devices from the welcome perspective and connecting the dots. For the last step, it was a bit of puzzling to figure out all the scenarios but I believe I’ve got them all:
When a device is added and there:
When a device is removed and:
Of course you can mix and match these, for example if you are configuring a device, disconnect it and then connect it again you’ll jump straight back into editing it.
When multiple devices are connected, you might want to configure more than one as well. It is inconvenient to have to close and open Piper again, so for this scenario I have given perspectives the option to declare whether they want a back button to be shown, which will take the user back to the welcome perspective to switch between devices:
Again we add another property to the perspectives interface:
@GObject.Property
def can_go_back(self):
"""Whether this perspective wants a back button to be displayed in case
there is more than one connected device."""
return True
This allows the window class to insert a back button into the perspectives' titlebars and prevents all perspectives of having to add it themselves:
def _add_perspective(self, perspective, ratbag):
self.stack_perspectives.add_named(perspective, perspective.name)
self.stack_titlebar.add_named(perspective.titlebar, perspective.name)
if perspective.can_go_back:
button_back = Gtk.Button.new_from_icon_name("go-previous-symbolic",
Gtk.IconSize.BUTTON)
button_back.set_visible(len(ratbag.devices) > 1)
button_back.connect("clicked", lambda button, ratbag:
self._present_welcome_perspective(ratbag.devices),
ratbag)
ratbag.connect("notify::devices", lambda ratbag, pspec:
button_back.set_visible(len(ratbag.devices) > 1))
perspective.titlebar.add(button_back)
# Place the button first in the titlebar.
perspective.titlebar.child_set_property(button_back, "position", 0)
That’s a small amount of code for another large dose o' polish!
In making ratbagd use enums everywhere as opposed to strings for some
properties, the ResolutionsPage
was left behind and couldn’t display which
buttons were assigned special mappings that relate to resolutions. I
fixed that this week; you can see
it work again in the videos above.
Last week I dropped indices from the UI, but this left the resolution rows looking quite empty. The solution is to simply make them less wide, although this might change when we cannot fix the range of the resolution scale logarithmically or by simply limitting it, as discussed in the linked pull request and this issue.
The last noteworthy change of this week is that the key capture for macros now also supports key releases, but this isn’t merged yet. We decided to drop button capture for now, as this isn’t (yet?) properly supported by libratbag.
That’s it for this week! The coming week will be more of the same: adding spit ‘n polish where it is needed the most.
This blog post is part of a series. You can read the next part about saving the planet here or the previous part about the welcome screen here.