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