roundabout,
created on Thursday, 24 July 2025, 09:46:12 (1753350372),
received on Saturday, 26 July 2025, 07:40:06 (1753515606)
Author identity: vlad <vlad.muntoiu@gmail.com>
6403b7fc4dc26ce2e7aa5a0ea6019aa5f7b5dac9
applets/clock/__init__.py
@@ -40,7 +40,7 @@ class ClockApplet(panorama_panel.Applet):
# Create the monthly calendar self.popover = Gtk.Popover() self.track_popover(self.popover)panorama_panel.track_popover(self.popover)self.calendar = Gtk.Calendar() self.calendar.set_show_week_numbers(True) self.popover.set_child(self.calendar)
@@ -56,7 +56,7 @@ class ClockApplet(panorama_panel.Applet):
self.set_time() self.context_menu = self.make_context_menu() self.track_popover(self.context_menu)panorama_panel.track_popover(self.context_menu)right_click_controller = Gtk.GestureClick() right_click_controller.set_button(3)
docs/applet-api/implementing-applets.md
@@ -0,0 +1,104 @@
How to implement applets for the Panorama panel =============================================== The Panorama panel loads Python packages from `~/local/share/panorama-panel/applets` and `~/usr/share/panorama-panel/applets`. Each of the loaded packages can provide one or more subclasses of `panorama_panel.Applet`, which is itself a subclass of `Gtk.Box`. The `panorama_panel` module is automatically made available to all modules the panel imports this way. Your subclass can set the class attributes `name` and `description` to be presented in menus. Try to make your class name reasonably unique, because the panel identifies applets by their `__name__` and doesn't support multiple classes sharing one. Managing configuration ---------------------- In the constructor you receive the configuration (a Python dictionary) as the third parameter. You also have to implement the `get_config(self)` method to return the configuration when asked to, that is, when the panel is saving its settings. This will be serialised to a YAML file. Using other sources of configuration for your applet is not recommended because there may be multiple applet instances. How you present the configuration graphically is entirely up to you. You can put `Gtk.Builder` files in your applet's directory to help you build the configuration window. This can be exposed in a context menu. Popover registration -------------------- Autohiding panels do not hide when a popover belonging to one is open. To implement this, all root popovers that should keep the panel open must be registered using the function `panorama_panel.track_popover(popover: Gtk.Popover)` which adds some signals to make sure the panel doesn't disappear while the popover is open. If you want the panel to stay open for some other reason, you can use the set `self.get_root().open_popovers` and add to it a unique integer identifier for your reason to keep the panel open — normally, this should be a Python `id()` for a relevant object to guarantee it is unique and easy to reproduce. Other functions --------------- The `panorama_panel` module provides a few other functions to use (when asked to pass an applet, pass `self`): * `get_panel_position(applet: Applet) -> Gtk.PositionType`: get the edge of the screen the panel is currently on (note that they may move, and you need to respond). It also provides two dictionaries: * `OPPOSITE_POSITION`: get the opposite `Gtk.PositionType` for another `Gtk.PositionType`. * `POSITION_TO_ARROW`: get the corresponding `Gtk.ArrowType` for a `Gtk.PositionType`. You can also override extra methods to get notified about other events: * `set_panel_position(self, position: Gtk.PositionType)`: called when the panel is repositioned. * `make_draggable(self)`: if you have dangerous controllers, you can disable them when edit mode is enabled. * `restore_drag(self)`: restore the state when edit mode is disabled. Of course, you can override any other `Gtk.Box` methods as well. Minimal example --------------- This applet displays a configurable string. It doesn't expose a configuration window. A more elaborate example with a configuration window, popovers and a menu button can be found at `applets/clock`. ~~~python import panorama_panel import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk class LabelApplet(panorama_panel.Applet): # Defining class properties name = "Label" description = "Show a text" def __init__(self, orientation=Gtk.Orientation.HORIZONTAL, config=None): super().__init__(orientation=orientation, config=config) if config is None: config = {} self.label = Gtk.Label() self.label.set_text(config.get("text", "Label")) self.append(self.label) def get_config(self): # It is not recommended to store a config dict in the applet, instead # generate it only when needed, to avoid duplicating state return {"text": self.label.get_text()} ~~~
shared/panorama_panel.py
@@ -52,23 +52,20 @@ class Applet(Gtk.Box):
def restore_drag(self): self.remove_controller(self.drag_source) def track_popover(self, popover: Gtk.Popover):popover.connect("show", lambda *args: _popover_shown(self, popover))popover.connect("closed", lambda *args: _popover_hidden(self, popover))def track_popover(popover: Gtk.Popover): popover.connect("show", lambda *args: _popover_shown(None, popover))popover.connect("closed", lambda *args: _popover_hidden(None, popover))popover.connect("show", lambda *args: _popover_shown(popover)) popover.connect("closed", lambda *args: _popover_hidden(popover))def _popover_shown(applet, popover: Gtk.Popover):def _popover_shown(popover: Gtk.Popover):popover.get_root().open_popovers.add(id(popover)) if popover.get_root().autohide: GLib.timeout_add(popover.get_root().hide_time // (popover.get_root().size - 1), popover.get_root().slide_in) def _popover_hidden(applet, popover: Gtk.Popover):def _popover_hidden(popover: Gtk.Popover):popover.get_root().open_popovers.remove(id(popover)) if popover.get_root().autohide and not popover.get_root().open_popovers: GLib.timeout_add(popover.get_root().hide_time // (popover.get_root().size - 1),