import Adw from 'gi://Adw'; import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; import { gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; import { get_supported_effects } from '../../effects/effects.js'; export const EffectRow = GObject.registerClass({ GTypeName: 'EffectRow', InternalChildren: [], }, class EffectRow extends Adw.ExpanderRow { constructor(effect, effects_dialog) { super({}); this.SUPPORTED_EFFECTS = get_supported_effects(_); this.effect = effect; this.effects_dialog = effects_dialog; this.pipeline_id = effects_dialog.pipeline_id; this.pipelines_manager = effects_dialog.pipelines_manager; if (effect.type in this.SUPPORTED_EFFECTS) { this.set_title(this.SUPPORTED_EFFECTS[effect.type].name); this.set_subtitle(this.SUPPORTED_EFFECTS[effect.type].description); this.populate_options(); } else { this._warn(`could not assign effect ${effect.type} to its correct name`); this.set_title(effect.type); } let prefix_bin = new Gtk.Box({ spacing: 6 }); this.add_prefix(prefix_bin); let move_bin = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, 'width-request': 38, 'height-request': 38, 'margin-top': 6, 'margin-bottom': 6 }); prefix_bin.append(move_bin); move_bin.add_css_class('linked'); this._move_up_button = new Gtk.Button({ 'icon-name': 'go-up-symbolic', 'width-request': 38, 'height-request': 19 }); this._move_down_button = new Gtk.Button({ 'icon-name': 'go-down-symbolic', 'width-request': 38, 'height-request': 19 }); this._move_up_button.add_css_class('flat'); this._move_down_button.add_css_class('flat'); move_bin.append(this._move_up_button); move_bin.append(this._move_down_button); this._move_up_button.connect('clicked', () => effects_dialog.move_row_by(this, -1)); this._move_down_button.connect('clicked', () => effects_dialog.move_row_by(this, +1)); let remove_button = new Gtk.Button({ 'icon-name': 'remove-row-symbolic', 'width-request': 38, 'height-request': 38, 'margin-top': 6, 'margin-bottom': 6, 'valign': Gtk.Align.CENTER }); prefix_bin.append(remove_button); remove_button.add_css_class('destructive-action'); remove_button.connect('clicked', () => effects_dialog.remove_row(this)); } populate_options() { const editable_params = this.SUPPORTED_EFFECTS[this.effect.type].editable_params; if (Object.keys(editable_params).length == 0) this.enable_expansion = false; for (const param_key in editable_params) { let param = editable_params[param_key]; let row; switch (param.type) { case "integer": row = new Adw.SpinRow({ adjustment: new Gtk.Adjustment({ lower: param.min, upper: param.max, step_increment: param.increment }) }); row.adjustment.set_value(this.get_effect_param(param_key)); row.adjustment.connect( 'value-changed', () => this.set_effect_param(param_key, row.adjustment.value) ); break; case "float": row = new Adw.ActionRow; let scale = new Gtk.Scale({ valign: Gtk.Align.CENTER, hexpand: true, width_request: 200, draw_value: true, value_pos: Gtk.PositionType.RIGHT, digits: param.digits, adjustment: new Gtk.Adjustment({ lower: param.min, upper: param.max, step_increment: param.increment, page_increment: param.big_increment }) }); // TODO check if it's a good idea to set the default parameter, as the "good" // value really change depending on the user wallpaper... if so, do for dynamic // blur too scale.add_mark( this.get_default_effect_param(param_key), Gtk.PositionType.BOTTOM, null ); row.add_suffix(scale); scale.adjustment.set_value(this.get_effect_param(param_key)); scale.adjustment.connect( 'value-changed', () => this.set_effect_param(param_key, scale.adjustment.value) ); break; case "boolean": row = new Adw.SwitchRow; row.set_active(this.get_effect_param(param_key)); row.connect( 'notify::active', () => this.set_effect_param(param_key, row.active) ); break; case "dropdown": row = new Adw.ComboRow({ model: new Gtk.StringList }); param.options.forEach(option => row.model.append(option)); row.selected = this.get_effect_param(param_key); row.connect( 'notify::selected', () => this.set_effect_param(param_key, row.selected) ); break; case "rgba": row = new Adw.ActionRow; let color_button = new Gtk.ColorButton({ valign: Gtk.Align.CENTER, width_request: 75, height_request: 45, show_editor: true, use_alpha: param.use_alpha }); row.add_suffix(color_button); // set original color let c = color_button.get_rgba().copy(); if (param.use_alpha) [c.red, c.green, c.blue, c.alpha] = this.get_effect_param(param_key); else [c.red, c.green, c.blue] = this.get_effect_param(param_key); color_button.set_rgba(c); // update on on 'color-set' color_button.connect( 'color-set', () => { let c = color_button.get_rgba(); if (param.use_alpha) this.set_effect_param(param_key, [c.red, c.green, c.blue, c.alpha]); else this.set_effect_param(param_key, [c.red, c.green, c.blue]); } ); break; default: row = new Adw.ActionRow; break; } row.set_title(param.name); row.set_subtitle(param.description); this.add_row(row); } } get_effect_param(key) { let effects = this.pipelines_manager.pipelines[this.pipeline_id].effects; const gsettings_effect = effects.find(e => e.id == this.effect.id); if ('params' in gsettings_effect && key in gsettings_effect.params) return gsettings_effect.params[key]; else return this.get_default_effect_param(key); } get_default_effect_param(key) { return this.SUPPORTED_EFFECTS[this.effect.type].class.default_params[key]; } set_effect_param(key, value) { // we must pay attention not to change the effects in the pipelines manager before updating // it in gsettings, else it won't be updated (or every effect will be) let effects = this.pipelines_manager.pipelines[this.pipeline_id].effects; const effect_index = effects.findIndex(e => e.id == this.effect.id); if (effect_index >= 0) { effects[effect_index] = { ...this.effect, params: { ...this.effect.params } }; effects[effect_index].params[key] = value; this.effect = effects[effect_index]; } else this._warn(`effect not found when setting key ${key}`); this.pipelines_manager.update_pipeline_effects(this.pipeline_id, effects, false); } _warn(str) { console.warn( `[Blur my Shell > effect row] pipeline '${this.pipeline_id}',` + ` effect '${this.effect.id}': ${str}` ); } });