roundabout,
created on Monday, 21 July 2025, 14:14:20 (1753107260),
received on Saturday, 26 July 2025, 07:40:05 (1753515605)
Author identity: vlad <vlad.muntoiu@gmail.com>
9642a19bc742abf6f8c433f6fb58c9637ede9d3a
main.py
@@ -16,18 +16,93 @@ import gi
gi.require_version("Gtk", "4.0") gi.require_version("Gtk4LayerShell", "1.0") from gi.repository import Gtk, GLib, Gtk4LayerShell, Gdk, Giofrom gi.repository import Gtk, GLib, Gtk4LayerShell, Gdk, Gio, GObjectsys.path.insert(0, str((Path(__file__).parent / "shared").resolve())) import panorama_panel @Gtk.Template(filename="panel-configurator.ui") class PanelConfigurator(Gtk.Frame): __gtype_name__ = "PanelConfigurator" panel_size_adjustment: Gtk.Adjustment = Gtk.Template.Child() monitor_number_adjustment: Gtk.Adjustment = Gtk.Template.Child() top_position_radio: Gtk.CheckButton = Gtk.Template.Child() bottom_position_radio: Gtk.CheckButton = Gtk.Template.Child() left_position_radio: Gtk.CheckButton = Gtk.Template.Child() right_position_radio: Gtk.CheckButton = Gtk.Template.Child() def __init__(self, panel: Panel, **kwargs): super().__init__(**kwargs) self.panel = panel self.panel_size_adjustment.set_value(panel.size) match self.panel.position: case Gtk.PositionType.TOP: self.top_position_radio.set_active(True) case Gtk.PositionType.BOTTOM: self.bottom_position_radio.set_active(True) case Gtk.PositionType.LEFT: self.left_position_radio.set_active(True) case Gtk.PositionType.RIGHT: self.right_position_radio.set_active(True) self.top_position_radio.panel_position_target = Gtk.PositionType.TOP 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 @Gtk.Template.Callback() def update_panel_size(self, adjustment: Gtk.Adjustment): if not self.get_root(): return self.panel.set_size(int(adjustment.get_value())) @Gtk.Template.Callback() def move_panel(self, button: Gtk.CheckButton): if not self.get_root(): return if not button.get_active(): return print("Moving panel") self.panel.set_position(button.panel_position_target) self.update_panel_size(self.panel_size_adjustment) # Make the applets aware of the changed orientation for area in (self.panel.left_area, self.panel.centre_area, self.panel.right_area): applet = area.get_first_child() while applet: applet.set_orientation(self.panel.get_orientation()) applet.queue_resize() applet = applet.get_next_sibling() @Gtk.Template.Callback() def move_to_monitor(self, adjustment: Gtk.Adjustment): if not self.get_root(): return app: PanoramaPanel = self.get_root().get_application() monitor = app.monitors[int(self.monitor_number_adjustment.get_value())] self.panel.unmap() Gtk4LayerShell.set_monitor(self.panel, monitor) self.panel.show() PANEL_POSITIONS_HUMAN = { Gtk.PositionType.TOP: "top", Gtk.PositionType.BOTTOM: "bottom", Gtk.PositionType.LEFT: "left", Gtk.PositionType.RIGHT: "right", } @Gtk.Template(filename="panel-manager.ui") class PanelManager(Gtk.Window): __gtype_name__ = "PanelManager" panel_editing_switch = Gtk.Template.Child()panel_editing_switch: Gtk.Switch = Gtk.Template.Child() panel_stack: Gtk.Stack = Gtk.Template.Child()def __init__(self, application: Gtk.Application, **kwargs): super().__init__(application=application, **kwargs)
@@ -47,6 +122,20 @@ class PanelManager(Gtk.Window):
self.connect("close-request", lambda *args: self.destroy()) if isinstance(self.get_application(), PanoramaPanel): app: PanoramaPanel = self.get_application() for panel in app.panels: configurator = PanelConfigurator(panel) self.panel_stack.add_child(configurator) configurator.monitor_number_adjustment.set_upper(len(app.monitors)) self.panel_stack.set_visible_child(self.panel_stack.get_first_child()) self.panel_stack.connect("notify::visible-child", self.set_visible_panel) self.panel_stack.notify("visible-child") def set_visible_panel(self, stack: Gtk.Stack, pspec: GObject.ParamSpec): panel: Panel = stack.get_visible_child().panel def set_edit_mode(self, switch, value): if isinstance(self.get_application(), PanoramaPanel): self.get_application().set_edit_mode(value)
@@ -87,30 +176,22 @@ class AppletArea(Gtk.Box):
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): super().__init__(application=application) self.set_default_size(800, size)self.set_decorated(False) self.position = positionself.position = Noneself.monitor_index = monitor_index self.size = sizeGtk4LayerShell.init_for_window(self) Gtk4LayerShell.set_monitor(self, monitor)Gtk4LayerShell.set_layer(self, Gtk4LayerShell.Layer.TOP) Gtk4LayerShell.auto_exclusive_zone_enable(self) Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.TOP, True)Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.LEFT, True)Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.RIGHT, True)box = Gtk.CenterBox() match position:case Gtk.PositionType.TOP | Gtk.PositionType.BOTTOM:box.set_orientation(Gtk.Orientation.HORIZONTAL)case Gtk.PositionType.LEFT | Gtk.PositionType.RIGHT:box.set_orientation(Gtk.Orientation.VERTICAL)self.set_child(box) self.set_position(position) self.set_size(size)self.left_area = AppletArea(orientation=box.get_orientation()) self.centre_area = AppletArea(orientation=box.get_orientation())
@@ -169,6 +250,44 @@ class Panel(Gtk.Window):
box = self.get_first_child() return box.get_orientation() def set_size(self, value: int): self.size = int(value) if self.get_orientation() == Gtk.Orientation.HORIZONTAL: self.set_size_request(800, self.size) self.set_default_size(800, self.size) else: self.set_size_request(self.size, 600) self.set_default_size(self.size, 600) def set_position(self, position: Gtk.PositionType): self.position = position match self.position: case Gtk.PositionType.TOP: Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.BOTTOM, False) Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.TOP, True) Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.LEFT, True) Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.RIGHT, True) case Gtk.PositionType.BOTTOM: Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.TOP, False) Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.BOTTOM, True) Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.LEFT, True) Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.RIGHT, True) case Gtk.PositionType.LEFT: Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.RIGHT, False) Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.LEFT, True) Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.TOP, True) Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.BOTTOM, True) case Gtk.PositionType.RIGHT: Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.LEFT, False) Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.RIGHT, True) Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.TOP, True) Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.BOTTOM, True) box = self.get_first_child() match self.position: case Gtk.PositionType.TOP | Gtk.PositionType.BOTTOM: box.set_orientation(Gtk.Orientation.HORIZONTAL) case Gtk.PositionType.LEFT | Gtk.PositionType.RIGHT: box.set_orientation(Gtk.Orientation.VERTICAL) def get_all_subclasses(klass: type) -> list[type]: subclasses = []
@@ -218,8 +337,8 @@ class PanoramaPanel(Gtk.Application):
super().__init__(application_id="com.roundabout_host.panorama.panel") self.display = Gdk.Display.get_default() self.monitors = self.display.get_monitors() self.applets_by_name = {}self.panels = []self.applets_by_name: dict[str, panorama_panel.Applet] = {} self.panels: list[Panel] = []self.manager_window = None self.edit_mode = False
@@ -243,6 +362,8 @@ class PanoramaPanel(Gtk.Application):
for panel_data in yaml_file["panels"]: position = PANEL_POSITIONS[panel_data["position"]] monitor_index = panel_data["monitor"] if monitor_index >= len(self.monitors): continuemonitor = self.monitors[monitor_index] size = panel_data["size"]
panel-configurator.ui
@@ -0,0 +1,175 @@
<?xml version='1.0' encoding='UTF-8'?> <!-- Created with Cambalache 0.96.1 --> <interface> <!-- interface-name panel-configurator.ui --> <requires lib="gtk" version="4.2"/> <template class="PanelConfigurator" parent="GtkFrame"> <property name="valign">start</property> <child> <object class="GtkFrame"> <property name="valign">start</property> <child> <object class="GtkListBox"> <property name="selection-mode">none</property> <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">Thic_kness</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="GtkScale"> <property name="adjustment"> <object class="GtkAdjustment" id="panel_size_adjustment"> <property name="page-increment">1.0</property> <property name="page-size">1.0</property> <property name="step-increment">1.0</property> <property name="upper">192.0</property> <property name="value">48.0</property> <signal name="value-changed" handler="update_panel_size"/> </object> </property> <property name="digits">0</property> <property name="hexpand">True</property> <property name="round-digits">0</property> <marks/> </object> </child> <child> <object class="GtkSpinButton" id="thickness_spinbutton"> <property name="adjustment">panel_size_adjustment</property> <property name="numeric">True</property> </object> </child> </object> </child> <child> <object class="GtkLabel"> <property name="label" translatable="yes">Depending on your theme and your applets, the panel may be larger than the set size.</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> <child> <object class="GtkGrid"> <property name="column-homogeneous">True</property> <property name="row-homogeneous">True</property> <child> <object class="GtkCheckButton" id="top_position_radio"> <property name="group">top_position_radio</property> <property name="label" translatable="yes">_Top</property> <property name="use-underline">True</property> <signal name="toggled" handler="move_panel"/> <layout> <property name="column">1</property> <property name="column-span">2</property> <property name="row">0</property> <property name="row-span">1</property> </layout> </object> </child> <child> <object class="GtkCheckButton" id="left_position_radio"> <property name="group">top_position_radio</property> <property name="label" translatable="yes">_Left</property> <property name="use-underline">True</property> <signal name="toggled" handler="move_panel"/> <layout> <property name="column">0</property> <property name="column-span">1</property> <property name="row">1</property> <property name="row-span">1</property> </layout> </object> </child> <child> <object class="GtkCheckButton" id="bottom_position_radio"> <property name="group">top_position_radio</property> <property name="label" translatable="yes">_Bottom</property> <property name="use-underline">True</property> <signal name="toggled" handler="move_panel"/> <layout> <property name="column">1</property> <property name="column-span">2</property> <property name="row">2</property> <property name="row-span">1</property> </layout> </object> </child> <child> <object class="GtkCheckButton" id="right_position_radio"> <property name="group">top_position_radio</property> <property name="label" translatable="yes">_Right</property> <property name="use-underline">True</property> <signal name="toggled" handler="move_panel"/> <layout> <property name="column">3</property> <property name="column-span">1</property> <property name="row">1</property> <property name="row-span">1</property> </layout> </object> </child> <child> <object class="GtkLabel"> <property name="label" translatable="yes">_Monitor number: </property> <property name="use-underline">True</property> <property name="xalign">-0.0</property> <layout> <property name="column">1</property> <property name="column-span">1</property> <property name="row">1</property> <property name="row-span">1</property> </layout> </object> </child> <child> <object class="GtkSpinButton" id="monitor_number_spinbutton"> <property name="adjustment"> <object class="GtkAdjustment" id="monitor_number_adjustment"> <property name="page-increment">1.0</property> <property name="page-size">1.0</property> <property name="step-increment">1.0</property> <property name="upper">64.0</property> <signal name="value-changed" handler="move_to_monitor"/> </object> </property> <layout> <property name="column">2</property> <property name="column-span">1</property> <property name="row">1</property> <property name="row-span">1</property> </layout> </object> </child> </object> </child> </object> </child> </object> </child> </object> </child> </template> </interface>
panel-manager.cmb
@@ -2,6 +2,6 @@
<!DOCTYPE cambalache-project SYSTEM "cambalache-project.dtd"> <!-- 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="aa48b67ba698d273b209d7ead4e7ef3f40511b9ab555cc29c88837999013b9ba"/><ui template-class="PanelConfigurator" filename="panel-configurator.ui" sha256="d247205b06581ddcde855e0e0e3b9cacda44d393117393bab8505455f04bc876"/><ui template-class="PanelManager" filename="panel-manager.ui" sha256="c16939e25edf779ee964dba64b4fc411ec8e7b45cb4c5707e403fca4c4be07e4"/> <ui template-class="PanelConfigurator" filename="panel-configurator.ui" sha256="3b362de1d7ee0e20a58d8412b6bea07af8f7199095aa47ef1d462ba808ae391b"/></cambalache-project>
panel-manager.ui
@@ -8,122 +8,83 @@
<property name="default-width">576</property> <property name="title" translatable="yes">Panel management</property> <child> <object class="GtkScrolledWindow"><object class="GtkBox"> <property name="orientation">vertical</property><child> <object class="GtkViewport"><object class="GtkCenterBox"> <property name="margin-bottom">8</property> <property name="margin-end">8</property> <property name="margin-start">8</property> <property name="margin-top">8</property> <child type="end"> <object class="GtkButton" id="next_panel_button"> <property name="action-name">win.next-panel</property> <property name="icon-name">go-next-symbolic</property> <property name="tooltip-text" translatable="yes">Next panel</property> </object> </child> <child type="start"> <object class="GtkButton" id="previous_panel_button"> <property name="action-name">win.previous-panel</property> <property name="has-tooltip">True</property> <property name="icon-name">go-previous-symbolic</property> <property name="tooltip-text" translatable="yes">Previous panel</property> </object> </child> </object> </child> <child> <object class="GtkScrolledWindow"> <property name="vexpand">True</property> <child> <object class="GtkViewport"> <child> <object class="GtkStack" id="panel_stack"> <property name="margin-end">16</property> <property name="margin-start">16</property> <property name="transition-type">slide-left-right</property> </object> </child> </object> </child> </object> </child> <child> <object class="GtkBox"> <property name="hexpand">True</property> <property name="margin-bottom">8</property> <property name="margin-end">8</property> <property name="margin-start">8</property> <property name="margin-top">8</property> <child> <object class="GtkButton" id="save_now_button"> <property name="has-tooltip">True</property> <property name="label" translatable="yes">_Save settings now</property> <property name="tooltip-text" translatable="yes">The configuration is also saved when exiting the app.</property> <property name="use-underline">True</property> <signal name="clicked" handler="save_settings"/> <style> <class name="suggested-action"/> </style> </object> </child><child> <object class="GtkBox"> <property name="margin-bottom">32</property><property name="margin-end">32</property><property name="margin-start">32</property><property name="margin-top">32</property><property name="orientation">vertical</property><property name="spacing">4</property><property name="halign">end</property><child> <object class="GtkFrame"><property name="label-widget"><object class="GtkLabel"><property name="label" translatable="yes">General options</property><attributes><attribute name="weight" value="bold"/></attributes></object></property><child><object class="GtkListBox"><property name="selection-mode">none</property><child><object class="GtkListBoxRow"><property name="action-name">win.toggle-edit-mode-switch</property><child><object class="GtkBox"><child><object class="GtkLabel"><property name="hexpand">True</property><property name="label" translatable="yes">Panel _editing mode</property><property name="mnemonic-widget">panel_editing_switch</property><property name="use-underline">True</property><property name="wrap">True</property><property name="xalign">0.0</property></object></child><child><object class="GtkSwitch" id="panel_editing_switch"><property name="halign">end</property><property name="valign">center</property></object></child></object></child></object></child><child><object class="GtkListBoxRow"><property name="activatable">False</property><child><object class="GtkBox"><property name="orientation">vertical</property><property name="spacing">4</property><child><object class="GtkButton" id="save_now_button"><property name="label" translatable="yes">_Save settings now</property><property name="use-underline">True</property><signal name="clicked" handler="save_settings"/><style><class name="suggested-action"/></style></object></child><child><object class="GtkLabel"><property name="hexpand">True</property><property name="label" translatable="yes">The configuration is also saved when exiting the app.</property><property name="wrap">True</property><property name="xalign">0.0</property></object></child></object></child></object></child></object></child><object class="GtkLabel"> <property name="hexpand">True</property> <property name="label" translatable="yes">Panel _editing mode</property> <property name="mnemonic-widget">panel_editing_switch</property> <property name="use-underline">True</property> <property name="wrap">True</property> <property name="xalign">0.0</property></object> </child> <child> <object class="GtkBox"><property name="orientation">vertical</property><property name="spacing">4</property><child><object class="GtkCenterBox"><child type="center"><object class="GtkLabel" id="current_panel_label"><property name="justify">center</property><property name="label" translatable="yes">No panel selected</property></object></child><child type="end"><object class="GtkButton" id="next_panel_button"><property name="action-name">win.next-panel</property><property name="icon-name">go-next-symbolic</property><property name="tooltip-text" translatable="yes">Next panel</property></object></child><child type="start"><object class="GtkButton" id="previous_panel_button"><property name="action-name">win.previous-panel</property><property name="has-tooltip">True</property><property name="icon-name">go-previous-symbolic</property><property name="tooltip-text" translatable="yes">Previous panel</property></object></child></object></child><child><object class="GtkStack"><property name="transition-type">slide-left-right</property></object></child><object class="GtkSwitch" id="panel_editing_switch"> <property name="halign">end</property> <property name="valign">center</property></object> </child> </object>