Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions settings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[/]
enable-move-keybindings=false
18 changes: 9 additions & 9 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ export default class TilingShellExtension extends Extension {
schema_id: 'org.gnome.mutter',
});
if (Settings.get_active_screen_edges()) {
debug('disable native edge tiling');
// disable native edge tiling
SettingsOverride.get().override(
gioSettings,
Expand All @@ -265,6 +266,7 @@ export default class TilingShellExtension extends Extension {
);
} else {
// bring back the value of native edge tiling
debug('bring back native edge tiling');
SettingsOverride.get().restoreKey(
gioSettings,
'edge-tiling',
Expand Down Expand Up @@ -574,19 +576,17 @@ export default class TilingShellExtension extends Extension {
this._dbus?.disable();
this._dbus = null;

this._fractionalScalingEnabled = false;

OverriddenWindowMenu.destroy();

// restore native edge tiling and all the overridden settings
SettingsOverride.destroy();

// destroy state and settings
GlobalState.destroy();
Settings.destroy();

// restore native edge tiling
SettingsOverride.get().restoreKey(
new Gio.Settings({ schema_id: 'org.gnome.mutter' }),
'edge-tiling',
);

this._fractionalScalingEnabled = false;

OverriddenWindowMenu.destroy();
debug('extension is disabled');
}
}
142 changes: 137 additions & 5 deletions src/prefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@ import Settings, { ActivationKey } from './settings/settings';
import { logger } from './utils/shell';
import { ExtensionPreferences } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
import Layout from '@components/layout/Layout';
import {
gettext as _,
ngettext,
pgettext,
} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
import SettingsExport from '@settings/settingsExport';
import { gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';

/* import Layout from "@/components/layout/Layout";
import Cairo from "@gi-types/cairo1";*/
Expand Down Expand Up @@ -659,6 +656,141 @@ export default class TilingShellExtensionPreferences extends ExtensionPreference
keybindingsDialogGroup.add(row);
});

// Import/export/reset section
const importExportGroup = new Adw.PreferencesGroup({
title: _('Import/Export/Reset'),
description: _('Import/export/reset the settings of Tiling Shell'),
});
prefsPage.add(importExportGroup);

const exportSettingsBtn = this._buildButtonRow(
_('Export settings'),
_('Export settings'),
_('Export settings to a file'),
() => {
const fc = new Gtk.FileChooserDialog({
title: _('Export settings to a text file'),
select_multiple: false,
action: Gtk.FileChooserAction.SAVE,
transientFor: window,
});
fc.set_current_folder(
Gio.File.new_for_path(GLib.get_home_dir()),
);
fc.add_button(_('Cancel'), Gtk.ResponseType.CANCEL);
fc.add_button(_('Save'), Gtk.ResponseType.OK);
fc.connect(
'response',
(_source: Gtk.FileChooserDialog, response_id: number) => {
try {
if (response_id === Gtk.ResponseType.OK) {
const file = _source.get_file();
if (!file) throw new Error('no file selected');

debug(
`Create file with path ${file.get_path()}`,
);
const settingsExport = new SettingsExport(
this.getSettings(),
);
const content = settingsExport.exportToString();
file.replace_contents_bytes_async(
new TextEncoder().encode(content),
null,
false,
Gio.FileCreateFlags.REPLACE_DESTINATION,
null,
(thisFile, res) => {
try {
thisFile?.replace_contents_finish(
res,
);
} catch (e) {
debug(e);
}
},
);
}
} catch (error: unknown) {
debug(error);
}

_source.destroy();
},
);

fc.present();
},
);
importExportGroup.add(exportSettingsBtn);

const importSettingsBtn = this._buildButtonRow(
_('Import settings'),
_('Import settings'),
_('Import settings from a file'),
() => {
const fc = new Gtk.FileChooserDialog({
title: _('Select a text file to import from'),
select_multiple: false,
action: Gtk.FileChooserAction.OPEN,
transientFor: window,
/* filter: new Gtk.FileFilter({
suffixes: ['txt'],
name: 'Text File',
}),*/
});
fc.set_current_folder(
Gio.File.new_for_path(GLib.get_home_dir()),
);
fc.add_button(_('Cancel'), Gtk.ResponseType.CANCEL);
fc.add_button(_('Open'), Gtk.ResponseType.OK);
fc.connect(
'response',
(_source: Gtk.FileChooserDialog, response_id: number) => {
try {
if (response_id === Gtk.ResponseType.OK) {
const file = _source.get_file();
if (!file) {
_source.destroy();
return;
}
debug(`Selected path ${file.get_path()}`);
const [success, content] =
file.load_contents(null);
if (success) {
const imported = new TextDecoder(
'utf-8',
).decode(content);
const settingsExport = new SettingsExport(
this.getSettings(),
);
settingsExport.importFromString(imported);
} else {
debug('Error while opening file');
}
}
} catch (error: unknown) {
debug(error);
}

_source.destroy();
},
);

fc.present();
},
);
importExportGroup.add(importSettingsBtn);

const resetSettingsBtn = this._buildButtonRow(
_('Reset settings'),
_('Reset settings'),
_('Bring back the default settings'),
() => new SettingsExport(this.getSettings()).restoreToDefault(),
'destructive-action',
);
importExportGroup.add(resetSettingsBtn);

// footer
const footerGroup = new Adw.PreferencesGroup();
prefsPage.add(footerGroup);
Expand Down
27 changes: 19 additions & 8 deletions src/settings/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,16 @@ export default class Settings {
}

static get_tiling_system_enabled(): boolean {
return this._settings?.get_boolean(this.SETTING_TILING_SYSTEM) ?? false;
return this._settings?.get_boolean(this.SETTING_TILING_SYSTEM) ?? true;
}

static get_snap_assist_enabled(): boolean {
return this._settings?.get_boolean(this.SETTING_SNAP_ASSIST) ?? false;
return this._settings?.get_boolean(this.SETTING_SNAP_ASSIST) ?? true;
}

static get_show_indicator(): boolean {
if (!this._settings) return true;
return this._settings.get_boolean(this.SETTING_SHOW_INDICATOR);
return this._settings.get_boolean(this.SETTING_SHOW_INDICATOR) ?? true;
}

static get_inner_gaps(scaleFactor: number = 1): {
Expand Down Expand Up @@ -148,7 +148,7 @@ export default class Settings {
static get_span_multiple_tiles(): boolean {
return (
this._settings?.get_boolean(this.SETTING_SPAN_MULTIPLE_TILES) ??
false
true
);
}

Expand Down Expand Up @@ -178,15 +178,15 @@ export default class Settings {
return (
this._settings?.get_boolean(
Settings.SETTING_RESTORE_WINDOW_ORIGINAL_SIZE,
) ?? false
) ?? true
);
}

static get_resize_complementing_windows(): boolean {
return (
this._settings?.get_boolean(
Settings.SETTING_RESIZE_COMPLEMENTING_WINDOWS,
) ?? false
) ?? true
);
}

Expand Down Expand Up @@ -225,7 +225,7 @@ export default class Settings {
static get_enable_move_keybindings(): boolean {
return (
this._settings?.get_boolean(this.SETTING_ENABLE_MOVE_KEYBINDINGS) ??
false
true
);
}

Expand All @@ -238,7 +238,7 @@ export default class Settings {
static get_active_screen_edges(): boolean {
return (
this._settings?.get_boolean(this.SETTING_ACTIVE_SCREEN_EDGES) ??
false
true
);
}

Expand Down Expand Up @@ -317,6 +317,17 @@ export default class Settings {
);
}

static set_enable_move_keybindings(value: boolean) {
this._settings?.set_boolean(
this.SETTING_ENABLE_MOVE_KEYBINDINGS,
value,
);
}

static set_active_screen_edges(value: boolean) {
this._settings?.set_boolean(this.SETTING_ACTIVE_SCREEN_EDGES, value);
}

static set_window_border_color(value: string): boolean {
return (
this._settings?.set_string(
Expand Down
84 changes: 84 additions & 0 deletions src/settings/settingsExport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import Settings from '@settings/settings';
import SettingsOverride from '@settings/settingsOverride';

const dconfPath = '/org/gnome/shell/extensions/tilingshell/';
const excludedKeys = [
Settings.SETTING_LAYOUTS_JSON,
Settings.SETTING_LAST_VERSION_NAME_INSTALLED,
Settings.SETTING_OVERRIDDEN_SETTINGS,
];

export default class SettingsExport {
private readonly _gioSettings: Gio.Settings;

constructor(gioSettings: Gio.Settings) {
this._gioSettings = gioSettings;
}

exportToString(): string {
return this._excludeKeys(this._dumpDconf());
}

importFromString(content: string) {
this.restoreToDefault();

const proc = Gio.Subprocess.new(
['dconf', 'load', dconfPath],
Gio.SubprocessFlags.STDIN_PIPE,
);

proc.communicate_utf8(content, null);

if (!proc.get_successful()) {
this.restoreToDefault();

throw new Error(
'Failed to import dconf dump file. Restoring to default...',
);
}
}

restoreToDefault() {
Settings.set_active_screen_edges(false);
Settings.set_enable_move_keybindings(false);
SettingsOverride.get().restoreAll();
this._gioSettings
.list_keys()
.filter((key) => key.length > 0 && !excludedKeys.includes(key))
.forEach((key) => this._gioSettings.reset(key));
}

private _dumpDconf(): string {
const proc = Gio.Subprocess.new(
['dconf', 'dump', dconfPath],
Gio.SubprocessFlags.STDOUT_PIPE,
);

const [, dump] = proc.communicate_utf8(null, null);

if (proc.get_successful()) return dump;
else throw new Error('Failed to dump dconf');
}

private _excludeKeys(dconfDump: string) {
if (dconfDump.length === 0) throw new Error('Empty dconf dump');

const keyFile = new GLib.KeyFile();
const length = new TextEncoder().encode(dconfDump).length;

if (!keyFile.load_from_data(dconfDump, length, GLib.KeyFileFlags.NONE))
throw new Error('Failed to load from dconf dump');

const [key_list] = keyFile.get_keys('/');

key_list.forEach((key) => {
if (excludedKeys.includes(key)) keyFile.remove_key('/', key);
});

const [data] = keyFile.to_data();
if (data) return data;
else throw new Error('Failed to exclude dconf keys');
}
}
Loading