By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

 main.py

View raw Download
text/x-script.python • 24.32 kiB
Python script, ASCII text executable
        
            
1
from __future__ import annotations
2
3
import os
4
import sys
5
import importlib
6
import time
7
import traceback
8
import typing
9
from itertools import accumulate, chain
10
from pathlib import Path
11
import ruamel.yaml as yaml
12
13
os.environ["GI_TYPELIB_PATH"] = "/usr/local/lib/x86_64-linux-gnu/girepository-1.0"
14
15
from ctypes import CDLL
16
CDLL('libgtk4-layer-shell.so')
17
18
import gi
19
gi.require_version("Gtk", "4.0")
20
gi.require_version("Gtk4LayerShell", "1.0")
21
22
from gi.repository import Gtk, GLib, Gtk4LayerShell, Gdk, Gio, GObject
23
24
sys.path.insert(0, str((Path(__file__).parent / "shared").resolve()))
25
26
import panorama_panel
27
28
29
custom_css = """
30
.panel-flash {
31
animation: flash 333ms ease-in-out 0s 2;
32
}
33
34
@keyframes flash {
35
0% {
36
background-color: initial;
37
}
38
50% {
39
background-color: #ffff0080;
40
}
41
0% {
42
background-color: initial;
43
}
44
}
45
"""
46
47
css_provider = Gtk.CssProvider()
48
css_provider.load_from_data(custom_css)
49
Gtk.StyleContext.add_provider_for_display(
50
Gdk.Display.get_default(),
51
css_provider,
52
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
53
)
54
55
56
@Gtk.Template(filename="panel-configurator.ui")
57
class PanelConfigurator(Gtk.Frame):
58
__gtype_name__ = "PanelConfigurator"
59
60
panel_size_adjustment: Gtk.Adjustment = Gtk.Template.Child()
61
monitor_number_adjustment: Gtk.Adjustment = Gtk.Template.Child()
62
top_position_radio: Gtk.CheckButton = Gtk.Template.Child()
63
bottom_position_radio: Gtk.CheckButton = Gtk.Template.Child()
64
left_position_radio: Gtk.CheckButton = Gtk.Template.Child()
65
right_position_radio: Gtk.CheckButton = Gtk.Template.Child()
66
autohide_switch: Gtk.Switch = Gtk.Template.Child()
67
68
def __init__(self, panel: Panel, **kwargs):
69
super().__init__(**kwargs)
70
self.panel = panel
71
self.panel_size_adjustment.set_value(panel.size)
72
73
match self.panel.position:
74
case Gtk.PositionType.TOP:
75
self.top_position_radio.set_active(True)
76
case Gtk.PositionType.BOTTOM:
77
self.bottom_position_radio.set_active(True)
78
case Gtk.PositionType.LEFT:
79
self.left_position_radio.set_active(True)
80
case Gtk.PositionType.RIGHT:
81
self.right_position_radio.set_active(True)
82
83
self.top_position_radio.panel_position_target = Gtk.PositionType.TOP
84
self.bottom_position_radio.panel_position_target = Gtk.PositionType.BOTTOM
85
self.left_position_radio.panel_position_target = Gtk.PositionType.LEFT
86
self.right_position_radio.panel_position_target = Gtk.PositionType.RIGHT
87
self.autohide_switch.set_active(self.panel.autohide)
88
89
@Gtk.Template.Callback()
90
def update_panel_size(self, adjustment: Gtk.Adjustment):
91
if not self.get_root():
92
return
93
self.panel.set_size(int(adjustment.get_value()))
94
95
@Gtk.Template.Callback()
96
def toggle_autohide(self, switch: Gtk.Switch, value: bool):
97
if not self.get_root():
98
return
99
self.panel.set_autohide(value, self.panel.hide_time)
100
101
@Gtk.Template.Callback()
102
def move_panel(self, button: Gtk.CheckButton):
103
if not self.get_root():
104
return
105
if not button.get_active():
106
return
107
print("Moving panel")
108
self.panel.set_position(button.panel_position_target)
109
self.update_panel_size(self.panel_size_adjustment)
110
111
# Make the applets aware of the changed orientation
112
for area in (self.panel.left_area, self.panel.centre_area, self.panel.right_area):
113
applet = area.get_first_child()
114
while applet:
115
applet.set_orientation(self.panel.get_orientation())
116
applet.set_panel_position(self.panel.position)
117
applet.queue_resize()
118
applet = applet.get_next_sibling()
119
120
@Gtk.Template.Callback()
121
def move_to_monitor(self, adjustment: Gtk.Adjustment):
122
if not self.get_root():
123
return
124
app: PanoramaPanel = self.get_root().get_application()
125
monitor = app.monitors[int(self.monitor_number_adjustment.get_value())]
126
self.panel.unmap()
127
Gtk4LayerShell.set_monitor(self.panel, monitor)
128
self.panel.show()
129
130
131
PANEL_POSITIONS_HUMAN = {
132
Gtk.PositionType.TOP: "top",
133
Gtk.PositionType.BOTTOM: "bottom",
134
Gtk.PositionType.LEFT: "left",
135
Gtk.PositionType.RIGHT: "right",
136
}
137
138
139
@Gtk.Template(filename="panel-manager.ui")
140
class PanelManager(Gtk.Window):
141
__gtype_name__ = "PanelManager"
142
143
panel_editing_switch: Gtk.Switch = Gtk.Template.Child()
144
panel_stack: Gtk.Stack = Gtk.Template.Child()
145
current_panel: typing.Optional[Panel] = None
146
147
def __init__(self, application: Gtk.Application, **kwargs):
148
super().__init__(application=application, **kwargs)
149
150
self.connect("close-request", lambda *args: self.destroy())
151
152
action_group = Gio.SimpleActionGroup()
153
154
self.next_panel_action = Gio.SimpleAction(name="next-panel")
155
action_group.add_action(self.next_panel_action)
156
self.next_panel_action.connect("activate", lambda *args: self.panel_stack.set_visible_child(self.panel_stack.get_visible_child().get_next_sibling()))
157
158
self.previous_panel_action = Gio.SimpleAction(name="previous-panel")
159
action_group.add_action(self.previous_panel_action)
160
self.previous_panel_action.connect("activate", lambda *args: self.panel_stack.set_visible_child(self.panel_stack.get_visible_child().get_prev_sibling()))
161
162
self.insert_action_group("win", action_group)
163
if isinstance(self.get_application(), PanoramaPanel):
164
self.panel_editing_switch.set_active(application.edit_mode)
165
self.panel_editing_switch.connect("state-set", self.set_edit_mode)
166
167
self.connect("close-request", lambda *args: self.unflash_old_panel())
168
self.connect("close-request", lambda *args: self.destroy())
169
170
if isinstance(self.get_application(), PanoramaPanel):
171
app: PanoramaPanel = self.get_application()
172
for panel in app.panels:
173
configurator = PanelConfigurator(panel)
174
self.panel_stack.add_child(configurator)
175
configurator.monitor_number_adjustment.set_upper(len(app.monitors))
176
177
self.panel_stack.set_visible_child(self.panel_stack.get_first_child())
178
self.panel_stack.connect("notify::visible-child", self.set_visible_panel)
179
self.panel_stack.notify("visible-child")
180
181
def unflash_old_panel(self):
182
if self.current_panel:
183
for area in (self.current_panel.left_area, self.current_panel.centre_area, self.current_panel.right_area):
184
area.unflash()
185
186
def set_visible_panel(self, stack: Gtk.Stack, pspec: GObject.ParamSpec):
187
self.unflash_old_panel()
188
189
panel: Panel = stack.get_visible_child().panel
190
191
self.current_panel = panel
192
self.next_panel_action.set_enabled(stack.get_visible_child().get_next_sibling() is not None)
193
self.previous_panel_action.set_enabled(stack.get_visible_child().get_prev_sibling() is not None)
194
195
# Start an animation to show the user what panel is being edited
196
for area in (panel.left_area, panel.centre_area, panel.right_area):
197
area.flash()
198
199
def set_edit_mode(self, switch, value):
200
if isinstance(self.get_application(), PanoramaPanel):
201
self.get_application().set_edit_mode(value)
202
203
@Gtk.Template.Callback()
204
def save_settings(self, *args):
205
if isinstance(self.get_application(), PanoramaPanel):
206
print("Saving settings as user requested")
207
self.get_application().save_config()
208
209
210
def get_applet_directories():
211
data_home = Path(os.getenv("XDG_DATA_HOME", Path.home() / ".local" / "share"))
212
data_dirs = [Path(d) for d in os.getenv("XDG_DATA_DIRS", "/usr/local/share:/usr/share").split(":")]
213
214
all_paths = [data_home / "panorama-panel" / "applets"] + [d / "panorama-panel" / "applets" for d in data_dirs]
215
return [d for d in all_paths if d.is_dir()]
216
217
218
def get_config_file():
219
config_home = Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config"))
220
221
return config_home / "panorama-panel" / "config.yaml"
222
223
224
class AppletArea(Gtk.Box):
225
def __init__(self, orientation=Gtk.Orientation.HORIZONTAL):
226
super().__init__()
227
228
self.drop_target = Gtk.DropTarget.new(GObject.TYPE_UINT64, Gdk.DragAction.MOVE)
229
self.drop_target.set_gtypes([GObject.TYPE_UINT64])
230
self.drop_target.connect("drop", self.drop_applet)
231
232
def drop_applet(self, drop_target: Gtk.DropTarget, value: int, x: float, y: float):
233
print(f"Dropping applet: {value}")
234
applet: panorama_panel.Applet = self.get_root().get_application().drags.pop(value)
235
print(f"Type: {applet.__class__.__name__}")
236
old_area: AppletArea = applet.get_parent()
237
old_area.remove(applet)
238
# Find the position where to insert the applet
239
child = self.get_first_child()
240
while child:
241
allocation = child.get_allocation()
242
child_x, child_y = self.translate_coordinates(self, 0, 0)
243
if self.get_orientation() == Gtk.Orientation.HORIZONTAL:
244
midpoint = child_x + allocation.width / 2
245
if x < midpoint:
246
applet.insert_before(self, child)
247
break
248
elif self.get_orientation() == Gtk.Orientation.VERTICAL:
249
midpoint = child_y + allocation.height / 2
250
if y < midpoint:
251
applet.insert_before(self, child)
252
break
253
child = child.get_next_sibling()
254
else:
255
self.append(applet)
256
applet.show()
257
return True
258
259
def set_edit_mode(self, value):
260
panel: Panel = self.get_root()
261
child = self.get_first_child()
262
while child is not None:
263
if value:
264
child.make_draggable()
265
else:
266
child.restore_drag()
267
child.set_opacity(0.75 if value else 1)
268
child = child.get_next_sibling()
269
270
if value:
271
self.add_controller(self.drop_target)
272
if panel.get_orientation() == Gtk.Orientation.HORIZONTAL:
273
self.set_size_request(48, 0)
274
elif panel.get_orientation() == Gtk.Orientation.VERTICAL:
275
self.set_size_request(0, 48)
276
else:
277
self.remove_controller(self.drop_target)
278
self.set_size_request(0, 0)
279
280
def flash(self):
281
self.add_css_class("panel-flash")
282
283
def unflash(self):
284
self.remove_css_class("panel-flash")
285
286
287
POSITION_TO_LAYER_SHELL_EDGE = {
288
Gtk.PositionType.TOP: Gtk4LayerShell.Edge.TOP,
289
Gtk.PositionType.BOTTOM: Gtk4LayerShell.Edge.BOTTOM,
290
Gtk.PositionType.LEFT: Gtk4LayerShell.Edge.LEFT,
291
Gtk.PositionType.RIGHT: Gtk4LayerShell.Edge.RIGHT,
292
}
293
294
295
class Panel(Gtk.Window):
296
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):
297
super().__init__(application=application)
298
self.drop_motion_controller = None
299
self.motion_controller = None
300
self.set_decorated(False)
301
self.position = None
302
self.autohide = None
303
self.monitor_index = monitor_index
304
self.hide_time = None
305
self.open_popovers: set[int] = set()
306
307
Gtk4LayerShell.init_for_window(self)
308
Gtk4LayerShell.set_monitor(self, monitor)
309
310
Gtk4LayerShell.set_layer(self, Gtk4LayerShell.Layer.TOP)
311
312
box = Gtk.CenterBox()
313
314
self.set_child(box)
315
self.set_position(position)
316
self.set_size(size)
317
self.set_autohide(autohide, hide_time)
318
319
self.left_area = AppletArea(orientation=box.get_orientation())
320
self.centre_area = AppletArea(orientation=box.get_orientation())
321
self.right_area = AppletArea(orientation=box.get_orientation())
322
323
box.set_start_widget(self.left_area)
324
box.set_center_widget(self.centre_area)
325
box.set_end_widget(self.right_area)
326
327
# Add a context menu
328
menu = Gio.Menu()
329
330
menu.append("Open _manager", "panel.manager")
331
332
self.context_menu = Gtk.PopoverMenu.new_from_model(menu)
333
self.context_menu.set_has_arrow(False)
334
self.context_menu.set_parent(self)
335
self.context_menu.set_halign(Gtk.Align.START)
336
self.context_menu.set_flags(Gtk.PopoverMenuFlags.NESTED)
337
panorama_panel.track_popover(self.context_menu)
338
339
right_click_controller = Gtk.GestureClick()
340
right_click_controller.set_button(3)
341
right_click_controller.connect("pressed", self.show_context_menu)
342
343
self.add_controller(right_click_controller)
344
345
action_group = Gio.SimpleActionGroup()
346
manager_action = Gio.SimpleAction.new("manager", None)
347
manager_action.connect("activate", self.show_manager)
348
action_group.add_action(manager_action)
349
self.insert_action_group("panel", action_group)
350
351
def reset_margins(self):
352
for edge in POSITION_TO_LAYER_SHELL_EDGE.values():
353
Gtk4LayerShell.set_margin(self, edge, 0)
354
355
def set_edit_mode(self, value):
356
for area in (self.left_area, self.centre_area, self.right_area):
357
area.set_edit_mode(value)
358
359
def show_context_menu(self, gesture, n_presses, x, y):
360
rect = Gdk.Rectangle()
361
rect.x = int(x)
362
rect.y = int(y)
363
rect.width = 1
364
rect.height = 1
365
366
self.context_menu.set_pointing_to(rect)
367
self.context_menu.popup()
368
369
def slide_in(self):
370
if not self.autohide:
371
Gtk4LayerShell.set_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position], 0)
372
return False
373
374
if Gtk4LayerShell.get_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position]) >= 0:
375
return False
376
377
Gtk4LayerShell.set_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position], Gtk4LayerShell.get_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position]) + 1)
378
return True
379
380
def slide_out(self):
381
if not self.autohide:
382
Gtk4LayerShell.set_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position], 0)
383
return False
384
385
if Gtk4LayerShell.get_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position]) <= 1 - self.size:
386
return False
387
388
Gtk4LayerShell.set_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position], Gtk4LayerShell.get_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position]) - 1)
389
return True
390
391
def show_manager(self, _0=None, _1=None):
392
print("Showing manager")
393
if self.get_application():
394
if not self.get_application().manager_window:
395
self.get_application().manager_window = PanelManager(self.get_application())
396
self.get_application().manager_window.connect("close-request", self.get_application().reset_manager_window)
397
self.get_application().manager_window.present()
398
399
def get_orientation(self):
400
box = self.get_first_child()
401
return box.get_orientation()
402
403
def set_size(self, value: int):
404
self.size = int(value)
405
if self.get_orientation() == Gtk.Orientation.HORIZONTAL:
406
self.set_size_request(800, self.size)
407
self.set_default_size(800, self.size)
408
else:
409
self.set_size_request(self.size, 600)
410
self.set_default_size(self.size, 600)
411
412
def set_position(self, position: Gtk.PositionType):
413
self.position = position
414
self.reset_margins()
415
match self.position:
416
case Gtk.PositionType.TOP:
417
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.BOTTOM, False)
418
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.TOP, True)
419
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.LEFT, True)
420
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.RIGHT, True)
421
case Gtk.PositionType.BOTTOM:
422
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.TOP, False)
423
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.BOTTOM, True)
424
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.LEFT, True)
425
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.RIGHT, True)
426
case Gtk.PositionType.LEFT:
427
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.RIGHT, False)
428
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.LEFT, True)
429
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.TOP, True)
430
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.BOTTOM, True)
431
case Gtk.PositionType.RIGHT:
432
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.LEFT, False)
433
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.RIGHT, True)
434
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.TOP, True)
435
Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.BOTTOM, True)
436
box = self.get_first_child()
437
match self.position:
438
case Gtk.PositionType.TOP | Gtk.PositionType.BOTTOM:
439
box.set_orientation(Gtk.Orientation.HORIZONTAL)
440
case Gtk.PositionType.LEFT | Gtk.PositionType.RIGHT:
441
box.set_orientation(Gtk.Orientation.VERTICAL)
442
443
if self.autohide:
444
if not self.open_popovers:
445
GLib.timeout_add(self.hide_time // (self.size - 1), self.slide_out)
446
447
def set_autohide(self, autohide: bool, hide_time: int):
448
self.autohide = autohide
449
self.hide_time = hide_time
450
if not self.autohide:
451
self.reset_margins()
452
Gtk4LayerShell.auto_exclusive_zone_enable(self)
453
if self.motion_controller is not None:
454
self.remove_controller(self.motion_controller)
455
if self.drop_motion_controller is not None:
456
self.remove_controller(self.drop_motion_controller)
457
self.motion_controller = None
458
self.drop_motion_controller = None
459
self.reset_margins()
460
else:
461
Gtk4LayerShell.set_exclusive_zone(self, 0)
462
# Only leave 1px of the window as a "mouse sensor"
463
Gtk4LayerShell.set_margin(self, POSITION_TO_LAYER_SHELL_EDGE[self.position], 1 - self.size)
464
self.motion_controller = Gtk.EventControllerMotion()
465
self.add_controller(self.motion_controller)
466
self.drop_motion_controller = Gtk.DropControllerMotion()
467
self.add_controller(self.drop_motion_controller)
468
469
self.motion_controller.connect("enter", lambda *args: GLib.timeout_add(self.hide_time // (self.size - 1), self.slide_in))
470
self.drop_motion_controller.connect("enter", lambda *args: GLib.timeout_add(self.hide_time // (self.size - 1), self.slide_in))
471
self.motion_controller.connect("leave", lambda *args: not self.open_popovers and GLib.timeout_add(self.hide_time // (self.size - 1), self.slide_out))
472
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))
473
474
def get_all_subclasses(klass: type) -> list[type]:
475
subclasses = []
476
for subclass in klass.__subclasses__():
477
subclasses.append(subclass)
478
subclasses += get_all_subclasses(subclass)
479
480
return subclasses
481
482
def load_packages_from_dir(dir_path: Path):
483
loaded_modules = []
484
485
for path in dir_path.iterdir():
486
if path.name.startswith("_"):
487
continue
488
489
if path.is_dir() and (path / "__init__.py").exists():
490
module_name = path.name
491
spec = importlib.util.spec_from_file_location(module_name, path / "__init__.py")
492
module = importlib.util.module_from_spec(spec)
493
spec.loader.exec_module(module)
494
loaded_modules.append(module)
495
else:
496
continue
497
498
return loaded_modules
499
500
501
PANEL_POSITIONS = {
502
"top": Gtk.PositionType.TOP,
503
"bottom": Gtk.PositionType.BOTTOM,
504
"left": Gtk.PositionType.LEFT,
505
"right": Gtk.PositionType.RIGHT,
506
}
507
508
509
PANEL_POSITIONS_REVERSE = {
510
Gtk.PositionType.TOP: "top",
511
Gtk.PositionType.BOTTOM: "bottom",
512
Gtk.PositionType.LEFT: "left",
513
Gtk.PositionType.RIGHT: "right",
514
}
515
516
517
class PanoramaPanel(Gtk.Application):
518
def __init__(self):
519
super().__init__(application_id="com.roundabout_host.panorama.panel")
520
self.display = Gdk.Display.get_default()
521
self.monitors = self.display.get_monitors()
522
self.applets_by_name: dict[str, panorama_panel.Applet] = {}
523
self.panels: list[Panel] = []
524
self.manager_window = None
525
self.edit_mode = False
526
self.drags = {}
527
528
def do_startup(self):
529
Gtk.Application.do_startup(self)
530
for i, monitor in enumerate(self.monitors):
531
geometry = monitor.get_geometry()
532
print(f"Monitor {i}: {geometry.width}x{geometry.height} at {geometry.x},{geometry.y}")
533
534
all_applets = list(chain.from_iterable(load_packages_from_dir(d) for d in get_applet_directories()))
535
print("Applets:")
536
subclasses = get_all_subclasses(panorama_panel.Applet)
537
for subclass in subclasses:
538
if subclass.__name__ in self.applets_by_name:
539
print(f"Name conflict for applet {subclass.__name__}. Only one will be loaded.", file=sys.stderr)
540
self.applets_by_name[subclass.__name__] = subclass
541
542
with open(get_config_file(), "r") as config_file:
543
yaml_loader = yaml.YAML(typ="rt")
544
yaml_file = yaml_loader.load(config_file)
545
for panel_data in yaml_file["panels"]:
546
position = PANEL_POSITIONS[panel_data["position"]]
547
monitor_index = panel_data["monitor"]
548
if monitor_index >= len(self.monitors):
549
continue
550
monitor = self.monitors[monitor_index]
551
size = panel_data["size"]
552
autohide = panel_data["autohide"]
553
hide_time = panel_data["hide_time"]
554
555
panel = Panel(self, monitor, position, size, monitor_index, autohide, hide_time)
556
self.panels.append(panel)
557
panel.show()
558
559
print(f"{size}px panel on {position} edge of monitor {monitor_index}, autohide is {autohide} ({hide_time}ms)")
560
561
for area_name, area in (("left", panel.left_area), ("centre", panel.centre_area), ("right", panel.right_area)):
562
applet_list = panel_data["applets"].get(area_name)
563
if applet_list is None:
564
continue
565
566
for applet in applet_list:
567
item = list(applet.items())[0]
568
AppletClass = self.applets_by_name[item[0]]
569
options = item[1]
570
applet_widget = AppletClass(orientation=panel.get_orientation(), config=options)
571
applet_widget.set_panel_position(panel.position)
572
573
area.append(applet_widget)
574
575
def do_activate(self):
576
Gio.Application.do_activate(self)
577
578
def save_config(self):
579
with open(get_config_file(), "w") as config_file:
580
yaml_writer = yaml.YAML(typ="rt")
581
data = {"panels": []}
582
for panel in self.panels:
583
panel_data = {
584
"position": PANEL_POSITIONS_REVERSE[panel.position],
585
"monitor": panel.monitor_index,
586
"size": panel.size,
587
"autohide": panel.autohide,
588
"hide_time": panel.hide_time,
589
"applets": {}
590
}
591
592
for area_name, area in (("left", panel.left_area), ("centre", panel.centre_area), ("right", panel.right_area)):
593
panel_data["applets"][area_name] = []
594
applet = area.get_first_child()
595
while applet is not None:
596
panel_data["applets"][area_name].append({
597
applet.__class__.__name__: applet.get_config(),
598
})
599
600
applet = applet.get_next_sibling()
601
602
data["panels"].append(panel_data)
603
604
yaml_writer.dump(data, config_file)
605
606
def do_shutdown(self):
607
print("Shutting down")
608
Gtk.Application.do_shutdown(self)
609
self.save_config()
610
611
def set_edit_mode(self, value):
612
self.edit_mode = value
613
for panel in self.panels:
614
panel.set_edit_mode(value)
615
616
def reset_manager_window(self, *args):
617
self.manager_window = None
618
619
620
if __name__ == "__main__":
621
app = PanoramaPanel()
622
app.run(sys.argv)
623