roundabout,
created on Thursday, 24 July 2025, 09:16:22 (1753348582),
received on Saturday, 26 July 2025, 07:40:06 (1753515606)
Author identity: vlad <vlad.muntoiu@gmail.com>
1e04534561d4a256b81ec2893bbc6a7311808e3a
applets/clock/__init__.py
@@ -40,6 +40,7 @@ class ClockApplet(panorama_panel.Applet):
# Create the monthly calendar self.popover = Gtk.Popover() self.track_popover(self.popover)self.calendar = Gtk.Calendar() self.calendar.set_show_week_numbers(True) self.popover.set_child(self.calendar)
@@ -55,6 +56,7 @@ class ClockApplet(panorama_panel.Applet):
self.set_time() self.context_menu = self.make_context_menu() self.track_popover(self.context_menu)right_click_controller = Gtk.GestureClick() right_click_controller.set_button(3)
config.yaml
@@ -2,6 +2,8 @@ panels:
- position: top monitor: 0 size: 40 autohide: false hide_time: 300applets: left: - LabelApplet:
main.py
@@ -3,6 +3,8 @@ from __future__ import annotations
import os import sys import importlib import time import tracebackimport typing from itertools import accumulate, chain from pathlib import Path
@@ -61,6 +63,7 @@ class PanelConfigurator(Gtk.Frame):
bottom_position_radio: Gtk.CheckButton = Gtk.Template.Child() left_position_radio: Gtk.CheckButton = Gtk.Template.Child() right_position_radio: Gtk.CheckButton = Gtk.Template.Child() autohide_switch: Gtk.Switch = Gtk.Template.Child()def __init__(self, panel: Panel, **kwargs): super().__init__(**kwargs)
@@ -81,6 +84,7 @@ class PanelConfigurator(Gtk.Frame):
self.bottom_position_radio.panel_position_target = Gtk.PositionType.BOTTOM self.left_position_radio.panel_position_target = Gtk.PositionType.LEFT self.right_position_radio.panel_position_target = Gtk.PositionType.RIGHT self.autohide_switch.set_active(self.panel.autohide)@Gtk.Template.Callback() def update_panel_size(self, adjustment: Gtk.Adjustment):
@@ -88,6 +92,12 @@ class PanelConfigurator(Gtk.Frame):
return self.panel.set_size(int(adjustment.get_value())) @Gtk.Template.Callback() def toggle_autohide(self, switch: Gtk.Switch, value: bool): if not self.get_root(): return self.panel.set_autohide(value, self.panel.hide_time) @Gtk.Template.Callback() def move_panel(self, button: Gtk.CheckButton): if not self.get_root():
@@ -274,25 +284,37 @@ class AppletArea(Gtk.Box):
self.remove_css_class("panel-flash") POSITION_TO_LAYER_SHELL_EDGE = { Gtk.PositionType.TOP: Gtk4LayerShell.Edge.TOP, Gtk.PositionType.BOTTOM: Gtk4LayerShell.Edge.BOTTOM, Gtk.PositionType.LEFT: Gtk4LayerShell.Edge.LEFT, Gtk.PositionType.RIGHT: Gtk4LayerShell.Edge.RIGHT, } class Panel(Gtk.Window): def __init__(self, application: Gtk.Application, monitor: Gdk.Monitor, position: Gtk.PositionType = Gtk.PositionType.TOP, size: int = 40, monitor_index: int = 0):def __init__(self, application: Gtk.Application, monitor: Gdk.Monitor, position: Gtk.PositionType = Gtk.PositionType.TOP, size: int = 40, monitor_index: int = 0, autohide: bool = False, hide_time: int = 0):super().__init__(application=application) self.drop_motion_controller = None self.motion_controller = Noneself.set_decorated(False) self.position = None self.autohide = Noneself.monitor_index = monitor_index self.hide_time = None self.open_popovers: set[int] = set()Gtk4LayerShell.init_for_window(self) Gtk4LayerShell.set_monitor(self, monitor) Gtk4LayerShell.set_layer(self, Gtk4LayerShell.Layer.TOP) Gtk4LayerShell.auto_exclusive_zone_enable(self)box = Gtk.CenterBox() self.set_child(box) self.set_position(position) self.set_size(size) self.set_autohide(autohide, hide_time)self.left_area = AppletArea(orientation=box.get_orientation()) self.centre_area = AppletArea(orientation=box.get_orientation())
@@ -312,6 +334,7 @@ class Panel(Gtk.Window):
self.context_menu.set_parent(self) self.context_menu.set_halign(Gtk.Align.START) self.context_menu.set_flags(Gtk.PopoverMenuFlags.NESTED) panorama_panel.track_popover(self.context_menu)right_click_controller = Gtk.GestureClick() right_click_controller.set_button(3)
@@ -325,6 +348,10 @@ class Panel(Gtk.Window):
action_group.add_action(manager_action) self.insert_action_group("panel", action_group) def reset_margins(self): for edge in POSITION_TO_LAYER_SHELL_EDGE.values(): Gtk4LayerShell.set_margin(self, edge, 0) def set_edit_mode(self, value): for area in (self.left_area, self.centre_area, self.right_area): area.set_edit_mode(value)
@@ -339,6 +366,28 @@ class Panel(Gtk.Window):
self.context_menu.set_pointing_to(rect) self.context_menu.popup() def slide_in(self): if not self.autohide: Gtk4LayerShell.set_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position], 0) return False if Gtk4LayerShell.get_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position]) >= 0: return False Gtk4LayerShell.set_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position], Gtk4LayerShell.get_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position]) + 1) return True def slide_out(self): if not self.autohide: Gtk4LayerShell.set_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position], 0) return False if Gtk4LayerShell.get_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position]) <= 1 - self.size: return False Gtk4LayerShell.set_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position], Gtk4LayerShell.get_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position]) - 1) return True def show_manager(self, _0=None, _1=None): print("Showing manager") if self.get_application():
@@ -362,6 +411,7 @@ class Panel(Gtk.Window):
def set_position(self, position: Gtk.PositionType): self.position = position self.reset_margins()match self.position: case Gtk.PositionType.TOP: Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.BOTTOM, False)
@@ -390,6 +440,37 @@ class Panel(Gtk.Window):
case Gtk.PositionType.LEFT | Gtk.PositionType.RIGHT: box.set_orientation(Gtk.Orientation.VERTICAL) if self.autohide: if not self.open_popovers: GLib.timeout_add(self.hide_time // (self.size - 1), self.slide_out) def set_autohide(self, autohide: bool, hide_time: int): self.autohide = autohide self.hide_time = hide_time if not self.autohide: self.reset_margins() Gtk4LayerShell.auto_exclusive_zone_enable(self) if self.motion_controller is not None: self.remove_controller(self.motion_controller) if self.drop_motion_controller is not None: self.remove_controller(self.drop_motion_controller) self.motion_controller = None self.drop_motion_controller = None self.reset_margins() else: Gtk4LayerShell.set_exclusive_zone(self, 0) # Only leave 1px of the window as a "mouse sensor" Gtk4LayerShell.set_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position], 1 - self.size) self.motion_controller = Gtk.EventControllerMotion() self.add_controller(self.motion_controller) self.drop_motion_controller = Gtk.DropControllerMotion() self.add_controller(self.drop_motion_controller) self.motion_controller.connect("enter", lambda *args: GLib.timeout_add(self.hide_time // (self.size - 1), self.slide_in)) self.drop_motion_controller.connect("enter", lambda *args: GLib.timeout_add(self.hide_time // (self.size - 1), self.slide_in)) self.motion_controller.connect("leave", lambda *args: not self.open_popovers and GLib.timeout_add(self.hide_time // (self.size - 1), self.slide_out)) self.drop_motion_controller.connect("leave", lambda *args: not self.open_popovers and GLib.timeout_add(self.hide_time // (self.size - 1), self.slide_out)) def get_all_subclasses(klass: type) -> list[type]: subclasses = [] for subclass in klass.__subclasses__():
@@ -468,12 +549,14 @@ class PanoramaPanel(Gtk.Application):
continue monitor = self.monitors[monitor_index] size = panel_data["size"] autohide = panel_data["autohide"] hide_time = panel_data["hide_time"]panel = Panel(self, monitor, position, size, monitor_index)panel = Panel(self, monitor, position, size, monitor_index, autohide, hide_time)self.panels.append(panel) panel.show() print(f"{size}px panel on {position} edge of monitor {monitor_index}")print(f"{size}px panel on {position} edge of monitor {monitor_index}, autohide is {autohide} ({hide_time}ms)")for area_name, area in (("left", panel.left_area), ("centre", panel.centre_area), ("right", panel.right_area)): applet_list = panel_data["applets"].get(area_name)
@@ -501,6 +584,8 @@ class PanoramaPanel(Gtk.Application):
"position": PANEL_POSITIONS_REVERSE[panel.position], "monitor": panel.monitor_index, "size": panel.size, "autohide": panel.autohide, "hide_time": panel.hide_time,"applets": {} }
panel-configurator.ui
@@ -32,6 +32,7 @@
<object class="GtkScale"> <property name="adjustment"> <object class="GtkAdjustment" id="panel_size_adjustment"> <property name="lower">12.0</property><property name="page-increment">1.0</property> <property name="page-size">1.0</property> <property name="step-increment">1.0</property>
@@ -68,6 +69,47 @@
</child> </object> </child> <child> <object class="GtkListBoxRow"> <property name="activatable">False</property> <child> <object class="GtkBox"> <property name="orientation">vertical</property> <child> <object class="GtkBox"> <child> <object class="GtkLabel"> <property name="label" translatable="yes">Automatically _hide</property> <property name="use-underline">True</property> <property name="width-request">128</property> <property name="wrap">True</property> <property name="xalign">-0.0</property> </object> </child> <child> <object class="GtkSwitch" id="autohide_switch"> <property name="halign">end</property> <property name="hexpand">True</property> <property name="valign">center</property> <signal name="state-set" handler="toggle_autohide"/> </object> </child> </object> </child> <child> <object class="GtkLabel"> <property name="label" translatable="yes">Only show the panel while the cursor is over it or a menu is being used</property> <property name="wrap">True</property> <property name="xalign">0.0</property> <attributes> <attribute name="weight" value="light"/> </attributes> </object> </child> </object> </child> </object> </child><child> <object class="GtkListBoxRow"> <property name="activatable">False</property>
panel-manager.cmb
@@ -3,5 +3,5 @@
<!-- Created with Cambalache 0.96.1 --> <cambalache-project version="0.96.0" target_tk="gtk-4.0"> <ui template-class="PanelManager" filename="panel-manager.ui" sha256="c16939e25edf779ee964dba64b4fc411ec8e7b45cb4c5707e403fca4c4be07e4"/> <ui template-class="PanelConfigurator" filename="panel-configurator.ui" sha256="3b362de1d7ee0e20a58d8412b6bea07af8f7199095aa47ef1d462ba808ae391b"/><ui template-class="PanelConfigurator" filename="panel-configurator.ui" sha256="fb3cd5709bd849ea837ee74978a7d74b6b0cbd6dbc46e4237cb8bd5173bcc295"/></cambalache-project>
shared/panorama_panel.py
@@ -1,7 +1,7 @@
import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk, Gdk, GObjectfrom gi.repository import Gtk, Gdk, GObject, GLibclass Applet(Gtk.Box):
@@ -52,6 +52,28 @@ 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)) def _popover_shown(applet, 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): 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), popover.get_root().slide_out) def get_panel_position(applet: Applet) -> Gtk.PositionType: return applet.get_root().position