Sonny's

Retro; the customizable clock widget is now available on Flathub in v2

Download on Flathub

This new release comes with

Support both 12h and 24h clock format. It follows GNOME Date & Time preference while being sandboxed thanks to libportal new API for the settings portal.

Energy usage has been improved by using a more efficient method to get the time and by making use of the magic GtkWindow.suspended property to stop updating the clock when the window is not visible.

Better support for round clocks. The new GTK renderer fixed the visual glitch on transparent corners caused by large border radius. Retro now restores window dimensions and disables the border radius on maximize to make it look good, no matter the shape.

Controls have been moved to a floating header bar to stay out of the way and prevent interference with customizations.

There are further improvements to do, but I decided to publish early because Retro was using GNOME 43 runtime which is end-of-life and I have limited time to spend on it.

Help welcome https://github.com/sonnyp/Retro/issues

Thanks to Gio.ListModel it is surprisingly simple to use a Gtk.ListBox as a vertical list of tabs for an Adw.TabView.

The benefit over using a simple Gtk.Stack is that Adw.TabView supports standard shortcuts for switching view.

Here is a GJS example that should translate well to other languages. You can try this directly in Workbench.

// This snippet goes into Workbench UI Blueprint

using Gtk 4.0;
using Adw 1;

Box {
  Gtk.ScrolledWindow {
    width-request: 200;
    Gtk.ListBox list_box {
      selection-mode: browse;
    }
  }
   
  Gtk.Separator {}
  
  Adw.TabView tab_view {}
}
// This snippet goes into Workbench Code JavaScript

import Gtk from "gi://Gtk?version=4.0";
import Adw from "gi://Adw?version=1";

import GObject from "gi://GObject";

Gtk.init();

const tab_view = workbench.builder.get_object("tab_view");
const list_box = workbench.builder.get_object("list_box");

// Create a binding between the Gtk.ListBox and the Adw.TabView

list_box.bind_model(
  tab_view.pages,
  // This function will be called for every new Adw.TabPage
  (tab_page) => {
    return buildTabRow(tab_page);
  },
);

list_box.connect("row-selected", (self, row) => {
  tab_view.set_selected_page(row.tab_page);
});

tab_view.connect("notify::selected-page", (self, page) => {
  list_box.select_row(tab_view.selected_page.list_box_row);
});

// Add some Adw.TabPage and let the binding do the rest

for (let i = 1; i < 11; i++) {
  const title = `hello ${i}`;
  const tab_page = tab_view.append(
    new Adw.StatusPage({
      title,
      hexpand: true,
    }),
  );
  tab_page.title = title;
}

// You probably should use a custom widget
// but this will do it for the purpose of this example

function buildTabRow(tab_page) {
  const list_box_row = new Gtk.ListBoxRow({
    selectable: true,
  });
  list_box_row.tab_page = tab_page;
  const label = new Gtk.Label();
  list_box_row.set_child(label);

  tab_page.list_box_row = list_box_row;

  tab_page.bind_property(
    "title",
    label,
    "label",
    GObject.BindingFlags.SYNC_CREATE,
  );

  return list_box_row;
}

Enter your email to subscribe to updates.