Skip to content
Open
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
52 changes: 50 additions & 2 deletions extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ export default class SearchLightExt extends Extension {
case 'border-radius':
break;
case 'shortcut-search':
this._updateShortcut();
if (this._shortcutsGrabbed) this._updateShortcut();
break;
case 'secondary-shortcut-search':
this._updateShortcut2();
if (this._shortcutsGrabbed) this._updateShortcut2();
break;
case 'window-effect': {
this._updateWindowEffect();
Expand Down Expand Up @@ -163,11 +163,17 @@ export default class SearchLightExt extends Extension {

this._updateShortcut();
this._updateShortcut2();
this._shortcutsGrabbed = true;
this._updateCss();

this._useAnimations = this._settings.get_boolean('use-animations');
this._animationSpeed = this._settings.get_double('animation-speed');

this._focusWindowId = global.display.connect(
'notify::focus-window',
this._onFocusWindowChanged.bind(this),
);

Main.overview.connectObject(
'overview-showing',
this._onOverviewShowing.bind(this),
Expand Down Expand Up @@ -222,6 +228,11 @@ export default class SearchLightExt extends Extension {
}

disable() {
if (this._focusWindowId) {
global.display.disconnect(this._focusWindowId);
this._focusWindowId = null;
}

this._hiTimer?.shutdown();
this._loTimer?.shutdown();
this._hiTimer = null;
Expand Down Expand Up @@ -894,6 +905,43 @@ export default class SearchLightExt extends Extension {
this._style.build('custom-search-light', styles);
}

_isFocusedAppBlacklisted() {
let dominated = this.blacklist_apps || [];
if (dominated.length === 0) return false;

let focusWindow = global.display.focus_window;
if (!focusWindow) return false;

let tracker = Shell.WindowTracker.get_default();
let app = tracker.get_window_app(focusWindow);
if (!app) return false;

let appId = app.get_id();
return dominated.includes(appId);
}

_onFocusWindowChanged() {
if (this._isFocusedAppBlacklisted()) {
this._ungrabShortcuts();
} else {
this._regrabShortcuts();
}
}

_ungrabShortcuts() {
if (this._shortcutsGrabbed === false) return;
this._shortcutsGrabbed = false;
this.accel.unlisten();
this.accel2.unlisten();
}

_regrabShortcuts() {
if (this._shortcutsGrabbed === true) return;
this._shortcutsGrabbed = true;
this._updateShortcut();
this._updateShortcut2();
}

_toggle_search_light() {
if (this._inOverview) return;
if (!this._visible) {
Expand Down
4 changes: 4 additions & 0 deletions preferences/keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ export const SettingsKeys = () => {
default_value: 100,
widget_type: 'scale',
},
'blacklist-apps': {
default_value: [],
widget_type: 'shortcut',
},
});

return settingsKeys;
Expand Down
234 changes: 233 additions & 1 deletion prefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import Gdk from 'gi://Gdk';
import Gtk from 'gi://Gtk';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import Adw from 'gi://Adw';

import { ShortcutSettingWidget } from './shortcuts.js';

Expand Down Expand Up @@ -103,7 +105,7 @@ export default class Preferences extends ExtensionPreferences {
}

addButtonEvents(window, builder, settings) {
//
this._setupBlacklist(window, builder, settings);
}

fillPreferencesWindow(window) {
Expand Down Expand Up @@ -163,6 +165,236 @@ export default class Preferences extends ExtensionPreferences {
this.updateMonitors();
}

_setupBlacklist(window, builder, settings) {
let group = builder.get_object('blacklist-group');
let addBtn = builder.get_object('blacklist-add-btn');

this._blacklistGroup = group;
this._blacklistSettings = settings;
this._blacklistWindow = window;
this._blacklistRows = [];
this._refreshBlacklistRows();

addBtn.connect('clicked', () => {
this._openAppChooser();
});
}

_refreshBlacklistRows() {
this._blacklistRows.forEach((row) => {
this._blacklistGroup.remove(row);
});
this._blacklistRows = [];

let apps = this._blacklistSettings.get_strv('blacklist-apps');
apps.forEach((appId) => {
let appInfo = Gio.DesktopAppInfo.new(appId);
let label = appInfo ? appInfo.get_display_name() : appId;

let row = new Adw.ActionRow({
title: label,
subtitle: appId,
});

if (appInfo && appInfo.get_icon()) {
let icon = new Gtk.Image({
gicon: appInfo.get_icon(),
pixel_size: 32,
margin_end: 8,
});
row.add_prefix(icon);
}

let removeBtn = new Gtk.Button({
icon_name: 'list-remove-symbolic',
valign: Gtk.Align.CENTER,
css_classes: ['flat'],
});
removeBtn.connect('clicked', () => {
let current = this._blacklistSettings.get_strv('blacklist-apps');
current = current.filter((id) => id !== appId);
this._blacklistSettings.set_strv('blacklist-apps', current);
this._refreshBlacklistRows();
});

row.add_suffix(removeBtn);
this._blacklistGroup.add(row);
this._blacklistRows.push(row);
});
}

_getRunningAppIds() {
let runningCmds = new Set();
try {
let procDir = Gio.File.new_for_path('/proc');
let enumerator = procDir.enumerate_children(
'standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE,
null,
);
let info;
while ((info = enumerator.next_file(null)) !== null) {
let name = info.get_name();
if (!/^\d+$/.test(name)) continue;
try {
let cmdlineFile = Gio.File.new_for_path(`/proc/${name}/cmdline`);
let [ok, contents] = cmdlineFile.load_contents(null);
if (ok) {
let cmdline = new TextDecoder().decode(contents).split('\0')[0];
let basename = cmdline.split('/').pop();
if (basename) runningCmds.add(basename);
}
} catch (e) {
continue;
}
}
} catch (e) {
return [];
}

let runningIds = [];
let allApps = Gio.AppInfo.get_all();
for (let app of allApps) {
if (!app.should_show()) continue;
let exe = app.get_executable();
if (exe && runningCmds.has(exe.split('/').pop())) {
runningIds.push(app.get_id());
}
}
return runningIds;
}

_openAppChooser() {
let dialog = new Adw.Window({
modal: true,
transient_for: this._blacklistWindow,
title: 'Select Application',
default_width: 400,
default_height: 500,
});

let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
let headerBar = new Adw.HeaderBar();
box.append(headerBar);

let searchEntry = new Gtk.SearchEntry({
placeholder_text: 'Filter applications\u2026',
margin_start: 12,
margin_end: 12,
margin_top: 6,
margin_bottom: 6,
});
box.append(searchEntry);

let loadingLabel = new Gtk.Label({
label: 'Loading application list\u2026',
vexpand: true,
valign: Gtk.Align.CENTER,
css_classes: ['dim-label'],
});
box.append(loadingLabel);

let scrolled = new Gtk.ScrolledWindow({
vexpand: true,
visible: false,
hscrollbar_policy: Gtk.PolicyType.NEVER,
});
let listBox = new Gtk.ListBox({
selection_mode: Gtk.SelectionMode.NONE,
});
listBox.set_filter_func((row) => {
let text = searchEntry.get_text().toLowerCase();
if (!text) return true;
if (!row._appName) return false;
return row._appName.toLowerCase().includes(text);
});
scrolled.set_child(listBox);
box.append(scrolled);

searchEntry.connect('search-changed', () => {
listBox.invalidate_filter();
});

dialog.set_content(box);
dialog.present();

GLib.timeout_add(GLib.PRIORITY_DEFAULT, 80, () => {
this._populateAppChooser(dialog, listBox);
loadingLabel.visible = false;
scrolled.visible = true;
return GLib.SOURCE_REMOVE;
});
}

_populateAppChooser(dialog, listBox) {
let allApps = Gio.AppInfo.get_all()
.filter((app) => app.should_show())
.sort((a, b) => a.get_display_name().localeCompare(b.get_display_name()));

let current = this._blacklistSettings.get_strv('blacklist-apps');
let runningIds = this._getRunningAppIds();

let runningApps = allApps.filter(
(app) => runningIds.includes(app.get_id()) && !current.includes(app.get_id()),
);
let otherApps = allApps.filter(
(app) => !runningIds.includes(app.get_id()) && !current.includes(app.get_id()),
);

let appendAppRow = (appInfo) => {
let appId = appInfo.get_id();
let row = new Adw.ActionRow({
title: appInfo.get_display_name(),
subtitle: appId,
});
row._appName = appInfo.get_display_name();

let gicon = appInfo.get_icon();
if (gicon) {
row.add_prefix(new Gtk.Image({ gicon, pixel_size: 32, margin_end: 8 }));
}

let addBtn = new Gtk.Button({
icon_name: 'list-add-symbolic',
valign: Gtk.Align.CENTER,
css_classes: ['flat'],
});
addBtn.connect('clicked', () => {
let cur = this._blacklistSettings.get_strv('blacklist-apps');
if (!cur.includes(appId)) {
cur.push(appId);
this._blacklistSettings.set_strv('blacklist-apps', cur);
this._refreshBlacklistRows();
}
dialog.close();
});

row.add_suffix(addBtn);
row.set_activatable_widget(addBtn);
listBox.append(row);
};

runningApps.forEach(appendAppRow);

if (runningApps.length > 0 && otherApps.length > 0) {
let separatorRow = new Gtk.ListBoxRow({
selectable: false,
activatable: false,
});
separatorRow.set_child(
new Gtk.Separator({
orientation: Gtk.Orientation.HORIZONTAL,
margin_top: 4,
margin_bottom: 4,
}),
);
separatorRow._appName = '';
listBox.append(separatorRow);
}

otherApps.forEach(appendAppRow);
}

updateMonitors() {
let monitors = this._monitorsConfig.monitors;
let count = monitors.length;
Expand Down
Binary file modified schemas/gschemas.compiled
Binary file not shown.
5 changes: 5 additions & 0 deletions schemas/org.gnome.shell.extensions.search-light.gschema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -132,5 +132,10 @@
<summary>Animation speed</summary>
<description>Change the speed of the appear and disappear window effect.</description>
</key>
<key type="as" name="blacklist-apps">
<default><![CDATA[[]]]></default>
<summary>Blacklisted applications</summary>
<description>List of application IDs where the shortcut is disabled when they have focus.</description>
</key>
</schema>
</schemalist>
19 changes: 19 additions & 0 deletions ui/general.ui
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,25 @@
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup" id="blacklist-group">
<property name="title" translatable="yes">Application Blacklist</property>
<property name="description" translatable="yes">Applications where the shortcut is disabled when focused.</property>
<child>
<object class="AdwActionRow" id="blacklist-apps-row">
<property name="title" translatable="yes">Blacklisted Apps</property>
<property name="subtitle" translatable="yes">Add applications to suppress the search shortcut (e.g. games).</property>
<child>
<object class="GtkButton" id="blacklist-add-btn">
<property name="icon-name">list-add-symbolic</property>
<property name="valign">center</property>
<property name="tooltip-text" translatable="yes">Add application</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<object class="GtkStringList" id="preferred-monitor-model">
<items>
Expand Down