1#!/usr/local/bin/python3.8
2
3from SettingsWidgets import SidePage
4from xapp.GSettingsWidgets import *
5from ChooserButtonWidgets import TweenChooserButton, EffectChooserButton
6
7EFFECT_SETS = {
8    "cinnamon": ("traditional", "traditional", "traditional", "none",  "none",  "none"),
9    "scale":    ("scale",       "scale",       "scale",       "scale", "scale", "scale"),
10    "fade":     ("fade",        "fade",        "fade",        "scale", "scale", "scale"),
11    "blend":    ("blend",       "blend",       "blend",       "scale", "scale", "scale"),
12    "move":     ("move",        "move",        "move",        "scale", "scale", "scale"),
13    "flyUp":    ("flyUp",       "flyDown",     "flyDown",     "scale", "scale", "scale"),
14    "flyDown":  ("flyDown",     "flyUp",       "flyUp",       "scale", "scale", "scale"),
15    "default":  ("scale",       "scale",       "none",        "none",  "none",  "none")
16}
17
18TRANSITIONS_SETS = {
19    "cinnamon": ("easeOutQuad",    "easeOutQuad",   "easeInQuad",  "easeInExpo", "easeNone",       "easeInQuad"),
20    "normal":   ("easeOutSine",    "easeInBack",    "easeInSine",  "easeInBack", "easeOutBounce",  "easeInBack"),
21    "extra":    ("easeOutElastic", "easeOutBounce", "easeOutExpo", "easeInExpo", "easeOutElastic", "easeInExpo"),
22    "fade":     ("easeOutQuart",   "easeInQuart",   "easeInQuart", "easeInBack", "easeOutBounce",  "easeInBack")
23}
24
25TIME_SETS = {
26    "cinnamon": (100, 120, 160, 100, 100, 100),
27    "slow":     (400, 400, 400, 100, 100, 100),
28    "normal":   (250, 250, 250, 100, 100, 100),
29    "fast":     (100, 100, 100, 100, 100, 100),
30    "default":  (250, 250, 150, 400, 400, 400)
31}
32
33COMBINATIONS = {
34    #  name           effect    transition    time
35    "cinnamon":   ("cinnamon", "cinnamon", "cinnamon"),
36    "scale":      ("scale",    "normal",   "normal"),
37    "fancyScale": ("scale",    "extra",    "slow"),
38    "fade":       ("fade",     "fade",     "normal"),
39    "blend":      ("blend",    "fade",     "normal"),
40    "move":       ("move",     "normal",   "fast"),
41    "flyUp":      ("flyUp",    "normal",   "fast"),
42    "flyDown":    ("flyDown",  "normal",   "fast"),
43    #for previous versions
44    "default":    ("default",  "normal",   "default")
45}
46
47OPTIONS = (
48    ("cinnamon",   _("Cinnamon")),
49    ("scale",      _("Scale")),
50    ("fancyScale", _("Fancy Scale")),
51    ("fade",       _("Fade")),
52    ("blend",      _("Blend")),
53    ("move",       _("Move")),
54    ("flyUp",      _("Fly up, down")),
55    ("flyDown",    _("Fly down, up")),
56    #for previous versions
57    ("default",    _("Default"))
58)
59TYPES = ("map", "close", "minimize", "maximize", "unmaximize", "tile")
60SCHEMA = "org.cinnamon"
61DEP_PATH = "org.cinnamon/desktop-effects"
62KEY_TEMPLATE = "desktop-effects-%s-%s"
63
64class GSettingsTweenChooserButton(TweenChooserButton, PXGSettingsBackend):
65    def __init__(self, schema, key, dep_key):
66        self.key = key
67        self.bind_prop = "tween"
68        self.bind_dir = Gio.SettingsBindFlags.DEFAULT
69        self.bind_object = self
70
71        if schema not in settings_objects:
72            settings_objects[schema] = Gio.Settings.new(schema)
73        self.settings = settings_objects[schema]
74
75        super(GSettingsTweenChooserButton, self).__init__()
76        self.bind_settings()
77
78class GSettingsEffectChooserButton(EffectChooserButton, PXGSettingsBackend):
79    def __init__(self, schema, key, dep_key, options):
80        self.key = key
81        self.bind_prop = "effect"
82        self.bind_dir = Gio.SettingsBindFlags.DEFAULT
83        self.bind_object = self
84
85        if schema not in settings_objects:
86            settings_objects[schema] = Gio.Settings.new(schema)
87        self.settings = settings_objects[schema]
88
89        super(GSettingsEffectChooserButton, self).__init__(options)
90        self.bind_settings()
91
92class Module:
93    name = "effects"
94    category = "appear"
95    comment = _("Control Cinnamon visual effects.")
96
97    def __init__(self, content_box):
98        keywords = _("effects, fancy, window")
99        sidePage = SidePage(_("Effects"), "cs-desktop-effects", keywords, content_box, module=self)
100        self.sidePage = sidePage
101
102    def on_module_selected(self):
103        if not self.loaded:
104            print("Loading Effects module")
105
106            self.sidePage.stack = SettingsStack()
107            self.sidePage.add_widget(self.sidePage.stack)
108
109            self.schema = Gio.Settings(SCHEMA)
110            self.effect_sets = {}
111            for name, sets in COMBINATIONS.items():
112                self.effect_sets[name] = (EFFECT_SETS[sets[0]], TRANSITIONS_SETS[sets[1]], TIME_SETS[sets[2]])
113
114            # Enable effects
115
116            page = SettingsPage()
117            self.sidePage.stack.add_titled(page, "effects", _("Enable effects"))
118
119            settings = page.add_section(_("Enable Effects"))
120
121            widget = GSettingsSwitch(_("Window effects"), "org.cinnamon.muffin", "desktop-effects")
122            settings.add_row(widget)
123
124            widget = GSettingsSwitch(_("Effects on dialog boxes"), "org.cinnamon", "desktop-effects-on-dialogs")
125            settings.add_reveal_row(widget, "org.cinnamon", "desktop-effects")
126
127            widget = GSettingsSwitch(_("Effects on menus"), "org.cinnamon", "desktop-effects-on-menus")
128            settings.add_reveal_row(widget, "org.cinnamon", "desktop-effects")
129
130            self.chooser = GSettingsComboBox(_("Effects style"), "org.cinnamon", "desktop-effects-style", OPTIONS)
131            self.chooser.content_widget.connect("changed", self.on_value_changed)
132            settings.add_reveal_row(self.chooser, "org.cinnamon", "desktop-effects")
133
134            widget = GSettingsSwitch(_("Fade effect on Cinnamon scrollboxes (like the Menu application list)"), "org.cinnamon", "enable-vfade")
135            settings.add_row(widget)
136
137            widget = GSettingsSwitch(_("Session startup animation"), "org.cinnamon", "startup-animation")
138            settings.add_row(widget)
139
140            self.schema.connect("changed::desktop-effects", self.on_desktop_effects_enabled_changed)
141
142            # Customize
143
144            page = SettingsPage()
145            self.sidePage.stack.add_titled(page, "customize", _("Customize"))
146
147            box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
148            label = Gtk.Label()
149            label.set_markup("<b>%s</b>" % _("Customize settings"))
150            box.pack_start(label, False, False, 0)
151            self.custom_switch = Gtk.Switch(active = self.is_custom())
152            box.pack_end(self.custom_switch, False, False, 0)
153            self.custom_switch.connect("notify::active", self.update_effects)
154            page.add(box)
155
156            self.revealer = Gtk.Revealer()
157            self.revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN)
158            self.revealer.set_transition_duration(150)
159            page.add(self.revealer)
160            settings = SettingsSection(_("Effect"))
161            self.revealer.add(settings)
162
163            self.size_group = Gtk.SizeGroup.new(Gtk.SizeGroupMode.HORIZONTAL)
164
165            effects = ["none", "scale", "fade", "blend", "move", "flyUp", "flyDown", "traditional"]
166
167            # MAPPING WINDOWS
168            widget = self.make_effect_group(_("Mapping windows"), "map", effects)
169            settings.add_row(widget)
170
171            # CLOSING WINDOWS
172            widget = self.make_effect_group(_("Closing windows"), "close", effects)
173            settings.add_row(widget)
174
175            # MINIMIZING WINDOWS
176            widget = self.make_effect_group(_("Minimizing windows"), "minimize", effects)
177            settings.add_row(widget)
178
179            # MAXIMIZING WINDOWS
180            # effects = ["none", _("None")], ["scale", _("Scale")]]
181            widget = self.make_effect_group(_("Maximizing windows"), "maximize")
182            settings.add_row(widget)
183
184            # UNMAXIMIZING WINDOWS
185            widget = self.make_effect_group(_("Unmaximizing windows"), "unmaximize")
186            settings.add_row(widget)
187
188            # TILING WINDOWS
189            widget = self.make_effect_group(_("Tiling and snapping windows"), "tile")
190            settings.add_row(widget)
191
192            self.update_effects(self.custom_switch, None)
193
194    def make_effect_group(self, group_label, key, effects=None):
195        tmin, tmax, tstep, tdefault = (0, 2000, 50, 200)
196
197        row = SettingsWidget()
198        row.set_spacing(5)
199
200        label = SettingsLabel()
201        label.set_margin_right(5)
202        label.set_markup(group_label)
203        label.props.xalign = 0.0
204        row.pack_start(label, False, False, 0)
205
206        label = Gtk.Label(_("ms"))
207        row.pack_end(label, False, False, 0)
208
209        effect = GSettingsEffectChooserButton(SCHEMA, KEY_TEMPLATE % (key, "effect"), DEP_PATH, effects)
210        self.size_group.add_widget(effect)
211        tween = GSettingsTweenChooserButton(SCHEMA, KEY_TEMPLATE % (key, "transition"), DEP_PATH)
212        self.size_group.add_widget(tween)
213        time = GSettingsSpinButton("", SCHEMA, KEY_TEMPLATE % (key, "time"), dep_key=DEP_PATH, mini=tmin, maxi=tmax, step=tstep, page=tdefault)
214        time.set_border_width(0)
215        time.set_margin_right(0)
216        time.set_margin_left(0)
217        time.set_spacing(0)
218        row.pack_end(time, False, False, 0)
219        row.pack_end(tween, False, False, 0)
220        row.pack_end(effect, False, False, 0)
221
222        return row
223
224    def is_custom(self):
225        effects = []
226        transitions = []
227        times = []
228
229        for i in TYPES:
230            effects.append(self.schema.get_string(KEY_TEMPLATE % (i, "effect")))
231            transitions.append(self.schema.get_string(KEY_TEMPLATE % (i, "transition")))
232            times.append(self.schema.get_int(KEY_TEMPLATE % (i, "time")))
233
234        value = (tuple(effects), tuple(transitions), tuple(times))
235        return value != self.effect_sets[self.chooser.value]
236
237    def on_value_changed(self, widget):
238        value = self.effect_sets[self.schema.get_string("desktop-effects-style")]
239        j = 0
240        for i in TYPES:
241            self.schema.set_string(KEY_TEMPLATE % (i, "effect"), value[0][j])
242            self.schema.set_string(KEY_TEMPLATE % (i, "transition"), value[1][j])
243            self.schema.set_int(KEY_TEMPLATE % (i, "time"), value[2][j])
244            j += 1
245
246    def update_effects(self, switch, gparam):
247        active = switch.get_active()
248
249        self.revealer.set_reveal_child(active)
250        #when unchecking the checkbutton, reset the values
251        if not active:
252            self.on_value_changed(self.chooser)
253
254    def on_desktop_effects_enabled_changed(self, schema, key):
255        active = schema.get_boolean(key)
256
257        if not active and schema.get_boolean("desktop-effects-on-dialogs"):
258            schema.set_boolean("desktop-effects-on-dialogs", False)
259
260        if not active and schema.get_boolean("desktop-effects-on-menus"):
261            schema.set_boolean("desktop-effects-on-menus", False)
262
263        self.update_effects(self.custom_switch, None)
264