-
-
Notifications
You must be signed in to change notification settings - Fork 128
/
extension.js
204 lines (168 loc) · 6.6 KB
/
extension.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import St from 'gi://St';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import * as Util from 'resource:///org/gnome/shell/misc/util.js';
import {
Utils, Settings, Gestures, Keybindings, LiveAltTab, Navigator,
Stackoverlay, Scratch, Workspace, Tiling, Topbar, Patches, App, Grab
} from './imports.js';
import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js';
/**
The currently used modules
- tiling is the main module, responsible for tiling and workspaces
- navigator is used to initiate a discrete navigation.
Focus is only switched when the navigation is done.
- keybindings is a utility wrapper around mutters keybinding facilities.
- scratch is used to manage floating windows, or scratch windows.
- liveAltTab is a simple altTab implementiation with live previews.
- stackoverlay is somewhat kludgy. It makes clicking on the left or right
edge of the screen always activate the partially (or sometimes wholly)
concealed window at the edges.
- app creates new windows based on the current application. It's possible
to create custom new window handlers.
- Patches is used for monkey patching gnome shell behavior which simply
doesn't fit paperwm.
- topbar adds the workspace name to the topbar and styles it.
- gestures is responsible for 3-finger swiping (only works in wayland).
Notes of ordering:
- several modules import settings, so settings should be before them;
- settings.js should not depend on other paperwm modules;
- Settings should be before Patches (for reverse order disable);
*/
export default class PaperWM extends Extension {
modules = [
Utils, Settings, Patches,
Gestures, Keybindings, LiveAltTab, Navigator, Stackoverlay, Scratch,
Workspace, Tiling, Topbar, App, Grab,
];
#userStylesheet = null;
enable() {
console.log(`#PaperWM enabled`);
this.enableUserConfig();
this.enableUserStylesheet();
// run enable method (with extension argument on all modules)
this.modules.forEach(m => {
if (m['enable']) {
m.enable(this);
}
});
}
disable() {
console.log('#PaperWM disabled');
this.prepareForDisable();
[...this.modules].reverse().forEach(m => {
if (m['disable']) {
m.disable();
}
});
this.disableUserStylesheet();
}
/**
* Prepares PaperWM for disable across modules.
*/
prepareForDisable() {
/**
* Finish any navigation (e.g. workspace switch view).
* Can put PaperWM in a breakable state of lock/disable
* while navigating.
*/
Navigator.finishNavigation();
}
getConfigDir() {
return Gio.file_new_for_path(`${GLib.get_user_config_dir()}/paperwm`);
}
configDirExists() {
return this.getConfigDir().query_exists(null);
}
hasUserConfigFile() {
return this.getConfigDir().get_child("user.js").query_exists(null);
}
hasUserStyleFile() {
return this.getConfigDir().get_child("user.css").query_exists(null);
}
/**
* Update the metadata.json in user config dir to always keep it up to date.
* We copy metadata.json to the config directory so gnome-shell-mode
* knows which extension the files belong to (ideally we'd symlink, but
* that trips up the importer: Extension.imports.<complete> in
* gnome-shell-mode crashes gnome-shell..)
*/
updateUserConfigFiles() {
if (!this.configDirExists()) {
return;
}
const configDir = this.getConfigDir();
try {
const metadata = this.dir.get_child("metadata.json");
metadata.copy(configDir.get_child("metadata.json"), Gio.FileCopyFlags.OVERWRITE, null, null);
} catch (error) {
console.error('PaperWM', `could not update user config metadata.json: ${error}`);
}
if (!this.hasUserStyleFile()) {
try {
const user = this.dir.get_child("config/user.css");
user.copy(configDir.get_child("user.css"), Gio.FileCopyFlags.NONE, null, null);
} catch (error) {
console.error('PaperWM', `could not update user config metadata.json: ${error}`);
}
}
}
installConfig() {
const configDir = this.getConfigDir();
// if user config folder doesn't exist, create it
if (!this.configDirExists()) {
configDir.make_directory_with_parents(null);
}
}
enableUserConfig() {
if (!this.configDirExists()) {
try {
this.installConfig();
const configDir = this.getConfigDir().get_path();
Main.notify("PaperWM", `Created user configuration folder: ${configDir}`);
} catch (e) {
Main.notifyError("PaperWM", `Failed create user configuration folder: ${e.message}`);
}
}
this.updateUserConfigFiles();
/* TODO: figure out something here
fmuellner:
> you can't
> as I said, it's part of gjs legacy imports
> you'll have to do something like const userMod = await import(${this.getConfigDir()}/user.js)
*/
/*
// add to searchpath if user has config file and action user.js
if (this.hasUserConfigFile()) {
let SearchPath = Extension.imports.searchPath;
let path = this.getConfigDir().get_path();
if (!SearchPath.includes(path)) {
SearchPath.push(path);
}
}
*/
}
/**
* Reloads user.css styles (if user.css present in ~/.config/paperwm).
*/
enableUserStylesheet() {
this.#userStylesheet = this.getConfigDir().get_child("user.css");
if (this.#userStylesheet.query_exists(null)) {
let themeContext = St.ThemeContext.get_for_stage(global.stage);
themeContext.get_theme().load_stylesheet(this.#userStylesheet);
}
}
/**
* Unloads user.css styles (if user.css present in ~/.config/paperwm).
*/
disableUserStylesheet() {
let themeContext = St.ThemeContext.get_for_stage(global.stage);
themeContext.get_theme().unload_stylesheet(this.#userStylesheet);
this.#userStylesheet = null;
}
spawnPager(content) {
const quoted = GLib.shell_quote(content);
Util.spawn(["sh", "-c", `echo -En ${quoted} | gedit --new-window -`]);
}
}