Compare commits

...

42 Commits

Author SHA1 Message Date
Florian Müllner
4345703c2e Bump version to 45.beta
Update NEWS.
2023-08-07 16:41:23 +02:00
Florian Müllner
a911447375 js: Port to ESM
The shell pulled the trigger and switched to ESM for all its
imports, follow suit.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/269>
2023-08-06 15:59:35 +02:00
Florian Müllner
2d3307c657 window-list: Use InjectionManager instead of custom classes
Once the shell is ported to ESM, it will no longer be possible
to replace entire classes (even when imported). Prepare for that
by overriding methods of the regular WorkspaceBackground class
instead.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/268>
2023-08-06 13:45:56 +02:00
Florian Müllner
d59bc0b7f0 window-list: Do not inject WindowPicker into Main
This will become impossible once Main is converted to ESM. Instead,
use the Extension class itself to hold the window picker.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/268>
2023-08-06 13:45:56 +02:00
Florian Müllner
cb8c2eb27f windowsNavigator: Use InjectionManager instead of custom classes
Once the shell is ported to ESM, it will no longer be possible
to replace entire classes (even when exported). Prepare for that
by overriding methods of the regular classes, instead of creating
custom subclasses.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/268>
2023-08-06 13:45:56 +02:00
Florian Müllner
0544729bba launch-new-instance: Use InjectionManager
The extension uses a straight-forward override that doesn't
benefit a lot from the new InjectionManager class, but let's
use the provided convenience API anyway.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/268>
2023-08-05 21:32:27 +02:00
Florian Müllner
017c410a6a native-window-placement: Use InjectionManager
The new convenience class was modelled after the code in the
extension, so it's a drop-in replacement.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/268>
2023-08-05 18:53:41 +02:00
Florian Müllner
f2c73329be extensions: Use new convenience classes
Convenience APIs for extensions are now provided as Extension/Prefs
base classes.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/268>
2023-08-05 18:53:41 +02:00
Efstathios Iosifidis
ce644be96f Update Greek translation 2023-08-01 20:41:56 +00:00
Florian Müllner
e75a1a15ac extensions: Import ExtensionUtils as module
ExtensionUtils has been converted to ESM and split into two modules,
for extensions and prefs respectively.

Adjust to those changes.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/266>
2023-07-15 14:13:25 +02:00
Florian Müllner
1155170c7c window-list: Stop using getCurrentExtension()
The method is no longer exported. There will be a nicer alternative
soon, in the meantime we can just keep track of our main Extension
object ourselves.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/266>
2023-07-15 14:10:27 +02:00
Florian Müllner
6d8f54a20b js: Really use connectObject()
I forgot in two places to change the actual connect() function
to connectObject() 🤦️

Fixes commit 3bfaf6f88a.
2023-07-10 07:22:25 +02:00
Florian Müllner
93a2e7bdba extensions: Stop using global.log()
It has been deprecated since 3.6(!) in favor of the actually
global log().

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/264>
2023-07-10 06:51:45 +02:00
Florian Müllner
3bfaf6f88a js: Use connectObject()
gnome-shell added (dis)connectObject() methods to partially automate
signal handling. It doesn't only save a significant amount of code,
but also makes it harder to miss cleaning up on destroy.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/263>
2023-07-09 18:45:07 +02:00
Florian Müllner
37baccd9fc window-list: Remove some dead code
The code that connected the signal was removed in 9fa522c29a.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/263>
2023-07-09 18:33:52 +02:00
Florian Müllner
9365725246 ci: Use wrapper to run eslint
The eslint job report its results as artifacts in junit format,
so that gitlab can present them in its UI.

However many psople miss that, and unsuccessfully check the logs
instead.

Address this by using a simplified version of gnome-shell's eslint
wrapper, so we can report results both on stdout and in a file
without re-running the linter.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/262>
2023-07-09 16:21:03 +02:00
Florian Müllner
f1257c4523 Ignore some common patterns
Ignore patches, vim session files and project configuration
of GNOME Builder and VSCode.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/261>
2023-07-09 15:59:34 +02:00
Florian Müllner
f0865f039e Clean up .gitignore
Meson enforces a separate build dir, so we no longer have to
care about build artifacts in the source tree. Same applies for
all the generated crap autotools like to spread around.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/261>
2023-07-09 15:59:34 +02:00
Florian Müllner
4955c20669 data: Remove left-over file
We no longer have a separate classic theme that could(*) use
custom assets, so the file is now very officially a left-over.

(*) spoiler alert: The made-up property where the image was
used has been ignored by gnome-shell for years

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/260>
2023-07-09 14:27:44 +02:00
Florian Müllner
cf007dd472 extensions: Turn extensions into modules
As gnome-shell is moving to ESM, it will now load extensions as
standard modules instead of using legacy imports. The change boils
down to exporting the Extension class as default, but we can also
start using standard imports for introspected modules now, so do
that at the same time.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/259>
2023-07-07 00:35:08 +02:00
Florian Müllner
701b14ecbf extensions: Use extension class for all extensions
This will be the only supported entry point when extension loading
switches to dynamic imports, so prepare for that by wrapping the
remaining standalone enable()/disable() methods in Extension classes.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/259>
2023-07-07 00:12:41 +02:00
Florian Müllner
18674b2e35 lint: Migrate eslint-plugin-jsdoc rule
Migrate a removed jsdoc, copied from the corresponding gnome-shell
change.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/259>
2023-07-07 00:11:57 +02:00
Florian Müllner
278d0afc79 Bump version to 45.alpha
Update NEWS.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/258>
2023-07-03 00:25:31 +02:00
Florian Müllner
90031432da build: Remove left-over variable
We no longer install a separate mode style, so the variable is
now unused.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/258>
2023-07-03 00:25:03 +02:00
Florian Müllner
b11f0f16f4 light-style: Always save scheme preference on enable()
Disable() should restore the scheme preference that was used when
the extension was enabled, not when it was first initialized.

Even if it's unlikely to be relevant in practice, let's make sure
we save the correct state.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/257>
2023-07-02 23:24:33 +02:00
Florian Müllner
b7895ad956 extensions: Add new light-style extension
Now that gnome-shell supports a light style, people may want to
use it without forcing all apps to be light.

Add a small extension that switches the default to light, so the
shell follows the regular "dark style" preference.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/256>
2023-06-23 14:36:09 +00:00
Arik W
22b9f888fb window-list: Add tooltip for long window titles
Adds a tooltip feature to the window buttons.
If a button’s label is too long to fit, a tooltip will show the complete content when the user hovers over the button.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/issues/170

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/251>
2023-06-18 19:08:53 +00:00
Florian Müllner
61a260bc94 places-menu: Account for app menu removal
The app menu has been removed from the top bar, so we can no longer
base our own indicator position on it.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/255>
2023-05-27 00:44:26 +02:00
Florian Müllner
ced3c94dfa classic: Account for removal of app menu
We no longer include the app menu in the regular GNOME session,
we shouldn't bring it back in Classic (not least because there
never was a similar menu in GNOME 2).

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/255>
2023-05-26 21:00:14 +02:00
Florian Müllner
904ead1fb1 window-list: Replace classic- with light style
Now that classic styling is based on color scheme instead of
a dedicated "classic" stylesheet, we should do the same for
extension styling, with the bonus that it also works with the
regular appearance setting outside the classic session.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/254>
2023-05-26 20:26:43 +02:00
Florian Müllner
f6b6049bc5 classic: Use light color scheme instead of classic styling
gnome-shell now includes a light variant, and supports switching
between dark- and light styling at runtime.

That means we no longer have to build our own stylesheet, and can
instead just instruct gnome-shell to always use the light style
in the classic session.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/254>
2023-05-26 20:26:43 +02:00
Alexander Weichart
ca1c4b0f9e window-list: Improve default stylesheet
Adjust colors to be more consistent with the current shell look.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/253>
2023-05-26 18:25:35 +02:00
Florian Müllner
58b4b3c8d6 Bump version to 44.0
Update NEWS.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/249>
2023-03-19 05:14:23 +01:00
Florian Müllner
25cc126ebc build: Add configuration summary
Meson now has a summary() function to easily summarize the build
configuration after the project was configured, use that for some
fancy output when the feature is available.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/249>
2023-03-19 04:52:56 +01:00
Florian Müllner
30bac19c5a build: Compile gschemas if necessary
It is good practice to recompile schemas after installing a new
schema, but for some reason we never did.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/249>
2023-03-19 04:30:45 +01:00
Florian Müllner
7689d660dc build: Bump meson requirement
Using the same minimum version as gnome-shell should be
uncontroversial, and allows us to use some new features.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/249>
2023-03-19 04:14:01 +01:00
Florian Müllner
e0d5ede296 Bump version to 44.rc
Update NEWS.
2023-03-06 04:35:46 +01:00
Florian Müllner
2c25e22145 Update sass submodule 2023-03-06 04:34:45 +01:00
Florian Müllner
ffa9806d40 Bump version to 44.beta
Update NEWS.
2023-02-14 18:01:27 +01:00
Florian Müllner
5ff41b9151 Update sass submodule 2023-02-14 17:59:37 +01:00
robxnano
7e8ba59304 Center-align all menus
Make popup menus center-aligned on their parent buttons, to
match the default GNOME Shell panel menus. Affects:
- Places menu
- Drive menu
- Workspace indicator
- Window menu workspace indicator

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/246>
2023-01-13 16:12:03 +00:00
Vasil Pupkin
497d175ae9 Update Belarusian translation 2022-11-27 18:57:09 +00:00
43 changed files with 1504 additions and 4870 deletions

30
.gitignore vendored
View File

@@ -1,29 +1,7 @@
ABOUT-NLS
Makefile
Makefile.in
Makefile.in.in
aclocal.m4
autom4te.cache/
config/
configure
config.log
config.status
data/*.json
m4/
po/*.header
po/*.sed
po/*.sin
po/Makevars.template
po/POTFILES
po/Rules-quot
po/gnome-shell-extensions.pot po/gnome-shell-extensions.pot
po/stamp-it
staging/
zip-files/ zip-files/
*~ *~
*.gmo *.patch
metadata.json *.sw?
*.desktop .buildconfig
*.gschema.valid .vscode
*.session

View File

@@ -107,7 +107,8 @@ eslint:
stage: review stage: review
<<: *prereview_req <<: *prereview_req
script: script:
- eslint -o $LINT_LOG -f junit --resolve-plugins-relative-to $(npm root -g) extensions - export NODE_PATH=$(npm root -g)
- ./.gitlab-ci/run-eslint --output-file ${LINT_LOG} --format junit --stdout
artifacts: artifacts:
paths: paths:
- ${LINT_LOG} - ${LINT_LOG}
@@ -132,8 +133,6 @@ fedora-build:
stage: build stage: build
needs: needs:
- build-fedora-container - build-fedora-container
variables:
GIT_SUBMODULE_STRATEGY: normal
script: script:
- meson setup build --werror -Dextension_set=all -Dclassic_mode=true - meson setup build --werror -Dextension_set=all -Dclassic_mode=true
- meson compile -C build - meson compile -C build

54
.gitlab-ci/run-eslint Executable file
View File

@@ -0,0 +1,54 @@
#!/usr/bin/env node
const {ESLint} = require('eslint');
console.log(`Running ESLint version ${ESLint.version}...`);
const fs = require('fs');
const path = require('path');
function hasOption(...names) {
return process.argv.some(arg => names.includes(arg));
}
function getOption(...names) {
const optIndex =
process.argv.findIndex(arg => names.includes(arg)) + 1;
if (optIndex === 0)
return undefined;
return process.argv[optIndex];
}
(async function main() {
const outputOption = getOption('--output-file', '-o');
const outputPath = outputOption ? path.resolve(outputOption) : null;
const sourceDir = path.dirname(process.argv[1]);
process.chdir(path.resolve(sourceDir, '..'));
const sources = ['extensions'];
const eslint = new ESLint();
const results = await eslint.lintFiles(sources);
const formatter = await eslint.loadFormatter(getOption('--format', '-f'));
const resultText = formatter.format(results);
if (outputPath) {
fs.mkdirSync(path.dirname(outputPath), {recursive: true});
fs.writeFileSync(outputPath, resultText);
if (hasOption('--stdout')) {
const consoleFormatter = await eslint.loadFormatter();
console.log(consoleFormatter.format(results));
}
} else {
console.log(resultText);
}
process.exitCode = results.some(r => r.errorCount > 0) ? 1 : 0;
})().catch((error) => {
process.exitCode = 1;
console.error(error);
});

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "data/gnome-shell-sass"]
path = data/gnome-shell-sass
url = https://gitlab.gnome.org/GNOME/gnome-shell-sass.git

40
NEWS
View File

@@ -1,3 +1,43 @@
45.beta
=======
* Port extensions to ESM [Florian; !259, !266, !268, !269]
* Misc. bug fixes and cleanups [Florian; !260, !261, !262, !263, !264]
Contributors:
Florian Müllner
Translators:
Efstathios Iosifidis [el]
45.alpha
========
* window-list: Modernize default styling [Alexander; !253]
* Replace classic styling with built-in light style [Florian; !254]
* window-list: Add tooltip for long window titles [Arik; !251]
* light-style: New extension [Florian; !256]
* Misc. bug fixes and cleanups [Florian; !255, !257]
Contributors:
Florian Müllner, Arik W, Alexander Weichart
44.0
====
* Bump version
44.rc
=====
* Bump version
44.beta
=======
* Tweak menu alignment [robxnano; !246]
Contributors:
Florian Müllner, robxnano
Translators:
Vasil Pupkin [be]
43.1 43.1
==== ====
* Fixed crash [Florian; !243] * Fixed crash [Florian; !243]

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 100 KiB

View File

@@ -1,10 +1,10 @@
{ {
"parentMode": "user", "parentMode": "user",
"stylesheetName": "gnome-classic.css", "colorScheme": "force-light",
"hasOverview": false, "hasOverview": false,
"showWelcomeDialog": false, "showWelcomeDialog": false,
"enabledExtensions": [@CLASSIC_EXTENSIONS@], "enabledExtensions": [@CLASSIC_EXTENSIONS@],
"panel": { "left": ["appMenu"], "panel": { "left": [],
"center": [], "center": [],
"right": ["a11y", "keyboard", "dateMenu", "quickSettings"] "right": ["a11y", "keyboard", "dateMenu", "quickSettings"]
} }

View File

@@ -1,5 +0,0 @@
@import url("gnome-classic.css");
stage {
-st-icon-style: symbolic;
}

View File

@@ -1,95 +0,0 @@
/* Use the gnome-shell theme, but with light colors */
$variant: 'light';
@import "gnome-shell-sass/_colors"; //use gtk colors
@import "gnome-shell-sass/_drawing";
@import "gnome-shell-sass/_common";
@import "gnome-shell-sass/_widgets";
/* Overrides */
#panel, #panel.solid {
font-weight: normal;
background-color: $bg_color;
background-gradient-direction: vertical;
background-gradient-end: darken($bg_color,5%);
border-top-color: #666; /* we don't support non-uniform border-colors and
use the top border color for any border, so we
need to set it even if all we want is a bottom
border */
border-bottom: 1px solid #666;
app-icon-bottom-clip: 0px;
&:overview {
background-color: #000;
background-gradient-end: #000;
border-top-color: #000;
border-bottom: 1px solid #000;
.panel-button { color: #fff; }
}
.panel-button {
-natural-hpadding: 8px;
-minimum-hpadding: 4px;
font-weight: normal;
color: $fg_color;
text-shadow: none;
transition-duration: 0ms;
border: 0;
border-radius: 0px;
&.clock-display {
.clock {
transition-duration: 0ms;
border: 0;
border-radius: 0px;
}
}
&:hover {
color: lighten($fg_color,10%);
text-shadow: none;
& .system-status-icon { icon-shadow: none; }
}
&:active, &:overview, &:focus, &:checked {
// Trick due to St limitations. It needs a background to draw
// a box-shadow
background-color: $selected_bg_color;
color: $selected_fg_color;
box-shadow: none;
& > .system-status-icon { icon-shadow: none; }
}
.app-menu-icon { width: 0; height: 0; margin: 0; } // shell's display:none; :D
.system-status-icon {
icon-shadow: none;
}
}
.panel-corner,
.panel-corner:active,
.panel-corner:overview,
.panel-corner:focus {
-panel-corner-radius: 0;
}
&.lock-screen,
&.unlock-screen,
&.login-screen {
background-color: transparentize($bg_color, 0.5);
background-gradient-start: transparentize($bg_color, 0.5);
background-gradient-end: transparentize($bg_color, 0.5);
border-bottom: none;
.panel-button { color: $osd_fg_color; }
}
}
#appMenu {
spinner-image: url("classic-process-working.svg");
.panel-status-menu-box { padding: 0; }
}
.tile-preview-left.on-primary,
.tile-preview-right.on-primary,
.tile-preview-left.tile-preview-right.on-primary {
/* keep in sync with -panel-corner-radius */
border-radius: 0;
}

View File

@@ -46,68 +46,5 @@ configure_file(
install_dir: modedir install_dir: modedir
) )
theme_sources = files(
'gnome-shell-sass/_colors.scss',
'gnome-shell-sass/_common.scss',
'gnome-shell-sass/_drawing.scss',
'gnome-shell-sass/_high-contrast-colors.scss',
'gnome-shell-sass/_widgets.scss',
'gnome-shell-sass/widgets/_a11y.scss',
'gnome-shell-sass/widgets/_app-grid.scss',
'gnome-shell-sass/widgets/_base.scss',
'gnome-shell-sass/widgets/_buttons.scss',
'gnome-shell-sass/widgets/_calendar.scss',
'gnome-shell-sass/widgets/_check-box.scss',
'gnome-shell-sass/widgets/_corner-ripple.scss',
'gnome-shell-sass/widgets/_dash.scss',
'gnome-shell-sass/widgets/_dialogs.scss',
'gnome-shell-sass/widgets/_entries.scss',
'gnome-shell-sass/widgets/_hotplug.scss',
'gnome-shell-sass/widgets/_ibus-popup.scss',
'gnome-shell-sass/widgets/_keyboard.scss',
'gnome-shell-sass/widgets/_login-dialog.scss',
'gnome-shell-sass/widgets/_looking-glass.scss',
'gnome-shell-sass/widgets/_message-list.scss',
'gnome-shell-sass/widgets/_misc.scss',
'gnome-shell-sass/widgets/_notifications.scss',
'gnome-shell-sass/widgets/_osd.scss',
'gnome-shell-sass/widgets/_overview.scss',
'gnome-shell-sass/widgets/_panel.scss',
'gnome-shell-sass/widgets/_popovers.scss',
'gnome-shell-sass/widgets/_quick-settings.scss',
'gnome-shell-sass/widgets/_screen-shield.scss',
'gnome-shell-sass/widgets/_scrollbars.scss',
'gnome-shell-sass/widgets/_search-entry.scss',
'gnome-shell-sass/widgets/_search-results.scss',
'gnome-shell-sass/widgets/_slider.scss',
'gnome-shell-sass/widgets/_switcher-popup.scss',
'gnome-shell-sass/widgets/_switches.scss',
'gnome-shell-sass/widgets/_window-picker.scss',
'gnome-shell-sass/widgets/_workspace-switcher.scss',
'gnome-shell-sass/widgets/_workspace-thumbnails.scss'
)
theme_data = [
'classic-process-working.svg',
'gnome-classic-high-contrast.css'
]
stylesheet = 'gnome-classic.css'
if fs.exists(stylesheet)
install_data(stylesheet, install_dir: themedir)
else
sassc = find_program('sassc', required: true)
custom_target(stylesheet,
input: fs.replace_suffix(stylesheet, '.scss'),
output: stylesheet,
depend_files: theme_sources,
command: [sassc, '-a', '@INPUT@', '@OUTPUT@'],
install: true,
install_dir: themedir
)
endif
install_data(theme_data, install_dir: themedir)
classic_override = '00_org.gnome.shell.extensions.classic.gschema.override' classic_override = '00_org.gnome.shell.extensions.classic.gschema.override'
install_data(classic_override, install_dir: schemadir) install_data(classic_override, install_dir: schemadir)

View File

@@ -1,18 +1,22 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
/* exported init enable disable */ import Atk from 'gi://Atk';
import Clutter from 'gi://Clutter';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GMenu from 'gi://GMenu';
import GObject from 'gi://GObject';
import Gtk from 'gi://Gtk';
import Meta from 'gi://Meta';
import Shell from 'gi://Shell';
import St from 'gi://St';
import {EventEmitter} from 'resource:///org/gnome/shell/misc/signals.js';
const { import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
Atk, Clutter, Gio, GLib, GMenu, GObject, Gtk, Meta, Shell, St,
} = imports.gi;
const {EventEmitter} = imports.misc.signals;
const DND = imports.ui.dnd; import * as DND from 'resource:///org/gnome/shell/ui/dnd.js';
const ExtensionUtils = imports.misc.extensionUtils; import * as Main from 'resource:///org/gnome/shell/ui/main.js';
const Main = imports.ui.main; import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
const PanelMenu = imports.ui.panelMenu; import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
const PopupMenu = imports.ui.popupMenu;
const _ = ExtensionUtils.gettext;
const appSys = Shell.AppSystem.get_default(); const appSys = Shell.AppSystem.get_default();
@@ -46,11 +50,8 @@ class ApplicationMenuItem extends PopupMenu.PopupBaseMenuItem {
this.label_actor = appLabel; this.label_actor = appLabel;
let textureCache = St.TextureCache.get_default(); let textureCache = St.TextureCache.get_default();
let iconThemeChangedId = textureCache.connect('icon-theme-changed', textureCache.connectObject('icon-theme-changed',
this._updateIcon.bind(this)); () => this._updateIcon(), this);
this.connect('destroy', () => {
textureCache.disconnect(iconThemeChangedId);
});
this._updateIcon(); this._updateIcon();
this._delegate = this; this._delegate = this;
@@ -269,9 +270,7 @@ class DesktopTarget extends EventEmitter {
_setDesktop(desktop) { _setDesktop(desktop) {
if (this._desktop) { if (this._desktop) {
this._desktop.disconnect(this._desktopDestroyedId); this._desktop.disconnectObject(this);
this._desktopDestroyedId = 0;
delete this._desktop._delegate; delete this._desktop._delegate;
} }
@@ -279,9 +278,9 @@ class DesktopTarget extends EventEmitter {
this.emit('desktop-changed'); this.emit('desktop-changed');
if (this._desktop) { if (this._desktop) {
this._desktopDestroyedId = this._desktop.connect('destroy', () => { this._desktop.connectObject('destroy', () => {
this._setDesktop(null); this._setDesktop(null);
}); }, this);
this._desktop._delegate = this; this._desktop._delegate = this;
} }
} }
@@ -321,10 +320,7 @@ class DesktopTarget extends EventEmitter {
} }
destroy() { destroy() {
if (this._windowAddedId) global.window_group.disconnectObject(this);
global.window_group.disconnect(this._windowAddedId);
this._windowAddedId = 0;
this._setDesktop(null); this._setDesktop(null);
} }
@@ -386,15 +382,14 @@ class ApplicationsButton extends PanelMenu.Button {
this.name = 'panelApplications'; this.name = 'panelApplications';
this.label_actor = this._label; this.label_actor = this._label;
this._showingId = Main.overview.connect('showing', () => { Main.overview.connectObject(
this.add_accessible_state(Atk.StateType.CHECKED); 'showing', () => this.add_accessible_state(Atk.StateType.CHECKED),
}); 'hiding', () => this.remove_accessible_state(Atk.StateType.CHECKED),
this._hidingId = Main.overview.connect('hiding', () => { this);
this.remove_accessible_state(Atk.StateType.CHECKED);
});
Main.wm.addKeybinding( Main.wm.addKeybinding(
'apps-menu-toggle-menu', 'apps-menu-toggle-menu',
ExtensionUtils.getSettings(), Extension.lookupByURL(import.meta.url).getSettings(),
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT, Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW, Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
() => this.menu.toggle()); () => this.menu.toggle());
@@ -410,15 +405,15 @@ class ApplicationsButton extends PanelMenu.Button {
}); });
this._tree = new GMenu.Tree({menu_basename: 'applications.menu'}); this._tree = new GMenu.Tree({menu_basename: 'applications.menu'});
this._treeChangedId = this._tree.connect('changed', this._tree.connectObject('changed',
this._onTreeChanged.bind(this)); () => this._onTreeChanged(), this);
this._applicationsButtons = new Map(); this._applicationsButtons = new Map();
this.reloadFlag = false; this.reloadFlag = false;
this._createLayout(); this._createLayout();
this._display(); this._display();
this._installedChangedId = appSys.connect('installed-changed', appSys.connectObject('installed-changed',
this._onTreeChanged.bind(this)); () => this._onTreeChanged(), this);
} }
_onTreeChanged() { _onTreeChanged() {
@@ -442,11 +437,7 @@ class ApplicationsButton extends PanelMenu.Button {
_onDestroy() { _onDestroy() {
super._onDestroy(); super._onDestroy();
Main.overview.disconnect(this._showingId); delete this._tree;
Main.overview.disconnect(this._hidingId);
appSys.disconnect(this._installedChangedId);
this._tree.disconnect(this._treeChangedId);
this._tree = null;
Main.wm.removeKeybinding('apps-menu-toggle-menu'); Main.wm.removeKeybinding('apps-menu-toggle-menu');
@@ -677,22 +668,17 @@ class ApplicationsButton extends PanelMenu.Button {
} }
} }
let appsMenuButton; export default class AppsMenuExtension extends Extension {
enable() {
this._appsMenuButton = new ApplicationsButton();
const index = Main.sessionMode.panel.left.indexOf('activities') + 1;
Main.panel.addToStatusArea(
'apps-menu', this._appsMenuButton, index, 'left');
}
/** */ disable() {
function enable() { Main.panel.menuManager.removeMenu(this._appsMenuButton.menu);
appsMenuButton = new ApplicationsButton(); this._appsMenuButton.destroy();
let index = Main.sessionMode.panel.left.indexOf('activities') + 1; delete this._appsMenuButton;
Main.panel.addToStatusArea('apps-menu', appsMenuButton, index, 'left'); }
}
/** */
function disable() {
Main.panel.menuManager.removeMenu(appsMenuButton.menu);
appsMenuButton.destroy();
}
/** */
function init() {
ExtensionUtils.initTranslations();
} }

View File

@@ -1,22 +1,20 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- // -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
// Start apps on custom workspaces // Start apps on custom workspaces
/* exported init enable disable */
const {Shell} = imports.gi; import Shell from 'gi://Shell';
const ExtensionUtils = imports.misc.extensionUtils; import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
const Main = imports.ui.main; import * as Main from 'resource:///org/gnome/shell/ui/main.js';
class WindowMover { class WindowMover {
constructor() { constructor(settings) {
this._settings = ExtensionUtils.getSettings(); this._settings = settings;
this._appSystem = Shell.AppSystem.get_default(); this._appSystem = Shell.AppSystem.get_default();
this._appConfigs = new Map(); this._appConfigs = new Map();
this._appData = new Map(); this._appData = new Map();
this._appsChangedId = this._appSystem.connectObject('installed-changed',
this._appSystem.connect('installed-changed', () => this._updateAppData(), this);
this._updateAppData.bind(this));
this._settings.connect('changed', this._updateAppConfigs.bind(this)); this._settings.connect('changed', this._updateAppConfigs.bind(this));
this._updateAppConfigs(); this._updateAppConfigs();
@@ -38,7 +36,7 @@ class WindowMover {
let removedApps = [...this._appData.keys()] let removedApps = [...this._appData.keys()]
.filter(a => !ids.includes(a.id)); .filter(a => !ids.includes(a.id));
removedApps.forEach(app => { removedApps.forEach(app => {
app.disconnect(this._appData.get(app).windowsChangedId); app.disconnectObject(this);
this._appData.delete(app); this._appData.delete(app);
}); });
@@ -46,21 +44,14 @@ class WindowMover {
.map(id => this._appSystem.lookup_app(id)) .map(id => this._appSystem.lookup_app(id))
.filter(app => app && !this._appData.has(app)); .filter(app => app && !this._appData.has(app));
addedApps.forEach(app => { addedApps.forEach(app => {
let data = { app.connectObject('window-changed',
windowsChangedId: app.connect('windows-changed', this._appWindowsChanged.bind(this), this);
this._appWindowsChanged.bind(this)), this._appData.set(app, {windows: app.get_windows()});
moveWindowsId: 0,
windows: app.get_windows(),
};
this._appData.set(app, data);
}); });
} }
destroy() { destroy() {
if (this._appsChangedId) { this._appSystem.disconnectObject(this);
this._appSystem.disconnect(this._appsChangedId);
this._appsChangedId = 0;
}
if (this._settings) { if (this._settings) {
this._settings.run_dispose(); this._settings.run_dispose();
@@ -105,47 +96,41 @@ class WindowMover {
} }
} }
let prevCheckWorkspaces; export default class AutoMoveExtension extends Extension {
let winMover; enable() {
this._prevCheckWorkspaces = Main.wm._workspaceTracker._checkWorkspaces;
/** */ Main.wm._workspaceTracker._checkWorkspaces =
function init() { this._getCheckWorkspaceOverride(this._prevCheckWorkspaces);
ExtensionUtils.initTranslations(); this._windowMover = new WindowMover(this.getSettings());
}
/**
* @returns {bool} - false (used as MetaLater handler)
*/
function myCheckWorkspaces() {
let keepAliveWorkspaces = [];
let foundNonEmpty = false;
for (let i = this._workspaces.length - 1; i >= 0; i--) {
if (!foundNonEmpty) {
foundNonEmpty = this._workspaces[i].list_windows().some(
w => !w.is_on_all_workspaces());
} else if (!this._workspaces[i]._keepAliveId) {
keepAliveWorkspaces.push(this._workspaces[i]);
}
} }
// make sure the original method only removes empty workspaces at the end disable() {
keepAliveWorkspaces.forEach(ws => (ws._keepAliveId = 1)); Main.wm._workspaceTracker._checkWorkspaces = this._prevCheckWorkspaces;
prevCheckWorkspaces.call(this); this._windowMover.destroy();
keepAliveWorkspaces.forEach(ws => delete ws._keepAliveId); delete this._windowMover;
}
return false; _getCheckWorkspaceOverride(originalMethod) {
} /* eslint-disable no-invalid-this */
return function () {
/** */ const keepAliveWorkspaces = [];
function enable() { let foundNonEmpty = false;
prevCheckWorkspaces = Main.wm._workspaceTracker._checkWorkspaces; for (let i = this._workspaces.length - 1; i >= 0; i--) {
Main.wm._workspaceTracker._checkWorkspaces = myCheckWorkspaces; if (!foundNonEmpty) {
foundNonEmpty = this._workspaces[i].list_windows().some(
winMover = new WindowMover(); w => !w.is_on_all_workspaces());
} } else if (!this._workspaces[i]._keepAliveId) {
keepAliveWorkspaces.push(this._workspaces[i]);
/** */ }
function disable() { }
Main.wm._workspaceTracker._checkWorkspaces = prevCheckWorkspaces;
winMover.destroy(); // make sure the original method only removes empty workspaces at the end
keepAliveWorkspaces.forEach(ws => (ws._keepAliveId = 1));
originalMethod.call(this);
keepAliveWorkspaces.forEach(ws => delete ws._keepAliveId);
return false;
};
/* eslint-enable no-invalid-this */
}
} }

View File

@@ -1,12 +1,13 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- // -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
// Start apps on custom workspaces // Start apps on custom workspaces
/* exported init buildPrefsWidget */
const {Adw, Gio, GLib, GObject, Gtk} = imports.gi; import Adw from 'gi://Adw';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Gtk from 'gi://Gtk';
const ExtensionUtils = imports.misc.extensionUtils; import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
const _ = ExtensionUtils.gettext;
const SETTINGS_KEY = 'application-list'; const SETTINGS_KEY = 'application-list';
@@ -59,13 +60,14 @@ class RulesList extends GObject.Object {
GObject.registerClass(this); GObject.registerClass(this);
} }
#settings = ExtensionUtils.getSettings(); #settings;
#rules = []; #rules = [];
#changedId; #changedId;
constructor() { constructor(settings) {
super(); super();
this.#settings = settings;
this.#changedId = this.#changedId =
this.#settings.connect(`changed::${SETTINGS_KEY}`, this.#settings.connect(`changed::${SETTINGS_KEY}`,
() => this.#sync()); () => this.#sync());
@@ -147,12 +149,13 @@ class AutoMoveSettingsWidget extends Adw.PreferencesGroup {
(self, name, param) => self._rules.changeWorkspace(...param.deepUnpack())); (self, name, param) => self._rules.changeWorkspace(...param.deepUnpack()));
} }
constructor() { constructor(settings) {
super({ super({
title: _('Workspace Rules'), title: _('Workspace Rules'),
}); });
this._rules = new RulesList(); this._settings = settings;
this._rules = new RulesList(this._settings);
const store = new Gio.ListStore({item_type: Gio.ListModel}); const store = new Gio.ListStore({item_type: Gio.ListModel});
const listModel = new Gtk.FlattenListModel({model: store}); const listModel = new Gtk.FlattenListModel({model: store});
@@ -173,7 +176,7 @@ class AutoMoveSettingsWidget extends Adw.PreferencesGroup {
} }
_addNewRule() { _addNewRule() {
const dialog = new NewRuleDialog(this.get_root()); const dialog = new NewRuleDialog(this.get_root(), this._settings);
dialog.connect('response', (dlg, id) => { dialog.connect('response', (dlg, id) => {
const appInfo = id === Gtk.ResponseType.OK const appInfo = id === Gtk.ResponseType.OK
? dialog.get_widget().get_app_info() : null; ? dialog.get_widget().get_app_info() : null;
@@ -312,13 +315,13 @@ class NewRuleDialog extends Gtk.AppChooserDialog {
GObject.registerClass(this); GObject.registerClass(this);
} }
constructor(parent) { constructor(parent, settings) {
super({ super({
transient_for: parent, transient_for: parent,
modal: true, modal: true,
}); });
this._settings = ExtensionUtils.getSettings(); this._settings = settings;
this.get_widget().set({ this.get_widget().set({
show_all: true, show_all: true,
@@ -338,14 +341,8 @@ class NewRuleDialog extends Gtk.AppChooserDialog {
} }
} }
/** */ export default class AutoMovePrefs extends ExtensionPreferences {
function init() { getPreferencesWidget() {
ExtensionUtils.initTranslations(); return new AutoMoveSettingsWidget(this.getSettings());
} }
/**
* @returns {Gtk.Widget} - the prefs widget
*/
function buildPrefsWidget() {
return new AutoMoveSettingsWidget();
} }

View File

@@ -1,14 +1,16 @@
/* exported init enable disable */
// Drive menu extension // Drive menu extension
const {Clutter, Gio, GObject, Shell, St} = imports.gi; import Clutter from 'gi://Clutter';
import Gio from 'gi://Gio';
import GObject from 'gi://GObject';
import Shell from 'gi://Shell';
import St from 'gi://St';
const ExtensionUtils = imports.misc.extensionUtils; import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const ShellMountOperation = imports.ui.shellMountOperation;
const _ = ExtensionUtils.gettext; import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
import * as ShellMountOperation from 'resource:///org/gnome/shell/ui/shellMountOperation.js';
Gio._promisify(Gio.File.prototype, 'query_filesystem_info_async'); Gio._promisify(Gio.File.prototype, 'query_filesystem_info_async');
@@ -47,19 +49,11 @@ class MountMenuItem extends PopupMenu.PopupBaseMenuItem {
this.hide(); this.hide();
this._changedId = mount.connect('changed', this._syncVisibility.bind(this)); mount.connectObject('changed',
() => this._syncVisibility(), this);
this._syncVisibility(); this._syncVisibility();
} }
_onDestroy() {
if (this._changedId) {
this.mount.disconnect(this._changedId);
this._changedId = 0;
}
super.destroy();
}
async _isInteresting() { async _isInteresting() {
if (!this.mount.can_eject() && !this.mount.can_unmount()) if (!this.mount.can_eject() && !this.mount.can_unmount())
return false; return false;
@@ -142,7 +136,7 @@ class DriveMenu extends PanelMenu.Button {
} }
constructor() { constructor() {
super(0.0, _('Removable devices')); super(0.5, _('Removable devices'));
let icon = new St.Icon({ let icon = new St.Icon({
icon_name: 'media-eject-symbolic', icon_name: 'media-eject-symbolic',
@@ -152,12 +146,12 @@ class DriveMenu extends PanelMenu.Button {
this.add_child(icon); this.add_child(icon);
this._monitor = Gio.VolumeMonitor.get(); this._monitor = Gio.VolumeMonitor.get();
this._addedId = this._monitor.connect('mount-added', this._monitor.connectObject(
(monitor, mount) => this._addMount(mount)); 'mount-added', (monitor, mount) => this._addMount(mount),
this._removedId = this._monitor.connect('mount-removed', (monitor, mount) => { 'mount-removed', (monitor, mount) => {
this._removeMount(mount); this._removeMount(mount);
this._updateMenuVisibility(); this._updateMenuVisibility();
}); }, this);
this._mounts = []; this._mounts = [];
@@ -199,33 +193,16 @@ class DriveMenu extends PanelMenu.Button {
} }
log('Removing a mount that was never added to the menu'); log('Removing a mount that was never added to the menu');
} }
}
_onDestroy() { export default class PlaceMenuExtension extends Extension {
if (this._addedId) { enable() {
this._monitor.disconnect(this._addedId); this._indicator = new DriveMenu();
this._monitor.disconnect(this._removedId); Main.panel.addToStatusArea('drive-menu', this._indicator);
this._addedId = 0; }
this._removedId = 0;
}
super._onDestroy(); disable() {
this._indicator.destroy();
delete this._indicator;
} }
} }
/** */
function init() {
ExtensionUtils.initTranslations();
}
let _indicator;
/** */
function enable() {
_indicator = new DriveMenu();
Main.panel.addToStatusArea('drive-menu', _indicator);
}
/** */
function disable() {
_indicator.destroy();
}

View File

@@ -1,17 +1,22 @@
/* exported enable disable */ import {AppIcon} from 'resource:///org/gnome/shell/ui/appDisplay.js';
const AppDisplay = imports.ui.appDisplay; import {InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js';
let _activateOriginal = null; export default class Extension {
constructor() {
this._injectionManager = new InjectionManager();
}
/** */ enable() {
function enable() { this._injectionManager.overrideMethod(AppIcon.prototype, 'activate',
_activateOriginal = AppDisplay.AppIcon.prototype.activate; originalMethod => {
AppDisplay.AppIcon.prototype.activate = function () { return function () {
_activateOriginal.call(this, 2); // eslint-disable-next-line no-invalid-this
}; originalMethod.call(this, 2);
} };
});
/** */ }
function disable() {
AppDisplay.AppIcon.prototype.activate = _activateOriginal; disable() {
this._injectionManager.clear();
}
} }

View File

@@ -0,0 +1,36 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import St from 'gi://St';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
export default class Extension {
_updateColorScheme(scheme) {
Main.sessionMode.colorScheme = scheme;
St.Settings.get().notify('color-scheme');
}
enable() {
this._savedColorScheme = Main.sessionMode.colorScheme;
this._updateColorScheme('prefer-light');
}
disable() {
this._updateColorScheme(this._savedColorScheme);
}
}

View File

@@ -0,0 +1,5 @@
extension_data += configure_file(
input: metadata_name + '.in',
output: metadata_name,
configuration: metadata_conf
)

View File

@@ -0,0 +1,10 @@
{
"extension-id": "@extension_id@",
"uuid": "@uuid@",
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"name": "Light Style",
"description": "Switch default to light style",
"shell-version": [ "@shell_current@" ],
"url": "@url@"
}

View File

@@ -1,11 +1,11 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- // -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
/* exported enable disable */ import Clutter from 'gi://Clutter';
const {Clutter} = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils; import {Extension, InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js';
const Main = imports.ui.main;
const {WindowPreview} = imports.ui.windowPreview; import * as Main from 'resource:///org/gnome/shell/ui/main.js';
const Workspace = imports.ui.workspace; import {WindowPreview} from 'resource:///org/gnome/shell/ui/windowPreview.js';
import * as Workspace from 'resource:///org/gnome/shell/ui/workspace.js';
// testing settings for natural window placement strategy: // testing settings for natural window placement strategy:
const WINDOW_PLACEMENT_NATURAL_ACCURACY = 20; // accuracy of window translate moves (KDE-default: 20) const WINDOW_PLACEMENT_NATURAL_ACCURACY = 20; // accuracy of window translate moves (KDE-default: 20)
@@ -236,75 +236,64 @@ class NaturalLayoutStrategy extends Workspace.LayoutStrategy {
} }
} }
let winInjections, workspaceInjections; export default class NativeWindowPlacementExtension extends Extension {
constructor(metadata) {
super(metadata);
/** */ this._injectionManager = new InjectionManager();
function resetState() { }
winInjections = { };
workspaceInjections = { }; enable() {
} const settings = this.getSettings();
/** */ const layoutProto = Workspace.WorkspaceLayout.prototype;
function enable() { const previewProto = WindowPreview.prototype;
resetState();
this._injectionManager.overrideMethod(layoutProto, '_createBestLayout', () => {
let settings = ExtensionUtils.getSettings(); /* eslint-disable no-invalid-this */
return function () {
workspaceInjections['_createBestLayout'] = Workspace.WorkspaceLayout.prototype._createBestLayout; this._layoutStrategy = new NaturalLayoutStrategy({
Workspace.WorkspaceLayout.prototype._createBestLayout = function (_area) { monitor: Main.layoutManager.monitors[this._monitorIndex],
this._layoutStrategy = new NaturalLayoutStrategy({ }, settings);
monitor: Main.layoutManager.monitors[this._monitorIndex], return this._layoutStrategy.computeLayout(this._sortedWindows);
}, settings); };
return this._layoutStrategy.computeLayout(this._sortedWindows); /* eslint-enable no-invalid-this */
}; });
// position window titles on top of windows in overlay // position window titles on top of windows in overlay
winInjections['_init'] = WindowPreview.prototype._init; this._injectionManager.overrideMethod(previewProto, '_init', originalMethod => {
WindowPreview.prototype._init = function (...args) { /* eslint-disable no-invalid-this */
winInjections['_init'].call(this, ...args); return function (...args) {
originalMethod.call(this, ...args);
if (!settings.get_boolean('window-captions-on-top'))
return; if (!settings.get_boolean('window-captions-on-top'))
return;
const alignConstraint = this._title.get_constraints().find(
c => c.align_axis && c.align_axis === Clutter.AlignAxis.Y_AXIS); const alignConstraint = this._title.get_constraints().find(
alignConstraint.factor = 0; c => c.align_axis && c.align_axis === Clutter.AlignAxis.Y_AXIS);
alignConstraint.factor = 0;
const bindConstraint = this._title.get_constraints().find(
c => c.coordinate && c.coordinate === Clutter.BindCoordinate.Y); const bindConstraint = this._title.get_constraints().find(
bindConstraint.offset = 0; c => c.coordinate && c.coordinate === Clutter.BindCoordinate.Y);
}; bindConstraint.offset = 0;
winInjections['_adjustOverlayOffsets'] = };
WindowPreview.prototype._adjustOverlayOffsets; /* eslint-enable no-invalid-this */
WindowPreview.prototype._adjustOverlayOffsets = function (...args) { });
winInjections['_adjustOverlayOffsets'].call(this, ...args);
this._injectionManager.overrideMethod(previewProto, '_adjustOverlayOffsets', originalMethod => {
if (settings.get_boolean('window-captions-on-top')) /* eslint-disable no-invalid-this */
this._title.translation_y = -this._title.translation_y; return function (...args) {
}; originalMethod.call(this, ...args);
}
if (settings.get_boolean('window-captions-on-top'))
/** this._title.translation_y = -this._title.translation_y;
* @param {object} object - object that was modified };
* @param {object} injection - the map of previous injections /* eslint-enable no-invalid-this */
* @param {string} name - the @injection key that should be removed });
*/ }
function removeInjection(object, injection, name) {
if (injection[name] === undefined) disable() {
delete object[name]; this._injectionManager.clear();
else global.stage.queue_relayout();
object[name] = injection[name]; }
}
/** */
function disable() {
var i;
for (i in workspaceInjections)
removeInjection(Workspace.WorkspaceLayout.prototype, workspaceInjections, i);
for (i in winInjections)
removeInjection(WindowPreview.prototype, winInjections, i);
global.stage.queue_relayout();
resetState();
} }

View File

@@ -1,17 +1,16 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
/* exported init enable disable */ import Clutter from 'gi://Clutter';
import GObject from 'gi://GObject';
import St from 'gi://St';
const {Clutter, GObject, St} = imports.gi; import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
const ExtensionUtils = imports.misc.extensionUtils; import * as Main from 'resource:///org/gnome/shell/ui/main.js';
const Main = imports.ui.main; import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
const PanelMenu = imports.ui.panelMenu; import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
const PopupMenu = imports.ui.popupMenu;
const Me = ExtensionUtils.getCurrentExtension(); import {PlacesManager} from './placeDisplay.js';
const PlaceDisplay = Me.imports.placeDisplay;
const _ = ExtensionUtils.gettext;
const N_ = x => x; const N_ = x => x;
const PLACE_ICON_SIZE = 16; const PLACE_ICON_SIZE = 16;
@@ -53,17 +52,8 @@ class PlaceMenuItem extends PopupMenu.PopupBaseMenuItem {
this.add_child(this._ejectButton); this.add_child(this._ejectButton);
} }
this._changedId = info.connect('changed', info.connectObject('changed',
this._propertiesChanged.bind(this)); this._propertiesChanged.bind(this), this);
}
destroy() {
if (this._changedId) {
this._info.disconnect(this._changedId);
this._changedId = 0;
}
super.destroy();
} }
activate(event) { activate(event) {
@@ -91,7 +81,7 @@ class PlacesMenu extends PanelMenu.Button {
} }
constructor() { constructor() {
super(0.0, _('Places')); super(0.5, _('Places'));
let label = new St.Label({ let label = new St.Label({
text: _('Places'), text: _('Places'),
@@ -100,7 +90,7 @@ class PlacesMenu extends PanelMenu.Button {
}); });
this.add_actor(label); this.add_actor(label);
this.placesManager = new PlaceDisplay.PlacesManager(); this.placesManager = new PlacesManager();
this._sections = { }; this._sections = { };
@@ -138,24 +128,18 @@ class PlacesMenu extends PanelMenu.Button {
} }
} }
/** */ export default class PlacesMenuExtension extends Extension {
function init() { enable() {
ExtensionUtils.initTranslations(); this._indicator = new PlacesMenu();
}
let pos = Main.sessionMode.panel.left.length;
let _indicator; if ('apps-menu' in Main.panel.statusArea)
pos++;
/** */ Main.panel.addToStatusArea('places-menu', this._indicator, pos, 'left');
function enable() { }
_indicator = new PlacesMenu();
disable() {
let pos = Main.sessionMode.panel.left.indexOf('appMenu'); this._indicator.destroy();
if ('apps-menu' in Main.panel.statusArea) delete this._indicator;
pos++; }
Main.panel.addToStatusArea('places-menu', _indicator, pos, 'left');
}
/** */
function disable() {
_indicator.destroy();
} }

View File

@@ -1,14 +1,14 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported PlacesManager */ import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import Shell from 'gi://Shell';
import {EventEmitter} from 'resource:///org/gnome/shell/misc/signals.js';
const {Gio, GLib, Shell} = imports.gi; import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
const {EventEmitter} = imports.misc.signals;
const ExtensionUtils = imports.misc.extensionUtils; import * as Main from 'resource:///org/gnome/shell/ui/main.js';
const Main = imports.ui.main; import * as ShellMountOperation from 'resource:///org/gnome/shell/ui/shellMountOperation.js';
const ShellMountOperation = imports.ui.shellMountOperation;
const _ = ExtensionUtils.gettext;
const N_ = x => x; const N_ = x => x;
Gio._promisify(Gio.AppInfo, 'launch_default_for_uri_async'); Gio._promisify(Gio.AppInfo, 'launch_default_for_uri_async');
@@ -248,7 +248,7 @@ const DEFAULT_DIRECTORIES = [
GLib.UserDirectory.DIRECTORY_VIDEOS, GLib.UserDirectory.DIRECTORY_VIDEOS,
]; ];
var PlacesManager = class extends EventEmitter { export class PlacesManager extends EventEmitter {
constructor() { constructor() {
super(); super();
@@ -260,15 +260,25 @@ var PlacesManager = class extends EventEmitter {
}; };
this._settings = new Gio.Settings({schema_id: BACKGROUND_SCHEMA}); this._settings = new Gio.Settings({schema_id: BACKGROUND_SCHEMA});
this._showDesktopIconsChangedId = this._settings.connect( this._settings.connectObject('changed::show-desktop-icons',
'changed::show-desktop-icons', this._updateSpecials.bind(this)); () => this._updateSpecials(), this);
this._updateSpecials(); this._updateSpecials();
/* /*
* Show devices, code more or less ported from nautilus-places-sidebar.c * Show devices, code more or less ported from nautilus-places-sidebar.c
*/ */
this._volumeMonitor = Gio.VolumeMonitor.get(); this._volumeMonitor = Gio.VolumeMonitor.get();
this._connectVolumeMonitorSignals(); this._volumeMonitor.connectObject(
'volume-added', () => this._updateMounts(),
'volume-removed', () => this._updateMounts(),
'volume-changed', () => this._updateMounts(),
'mount-added', () => this._updateMounts(),
'mount-removed', () => this._updateMounts(),
'mount-changed', () => this._updateMounts(),
'drive-connected', () => this._updateMounts(),
'drive-disconnected', () => this._updateMounts(),
'drive-changed', () => this._updateMounts(),
this);
this._updateMounts(); this._updateMounts();
this._bookmarksFile = this._findBookmarksFile(); this._bookmarksFile = this._findBookmarksFile();
@@ -293,34 +303,11 @@ var PlacesManager = class extends EventEmitter {
} }
} }
_connectVolumeMonitorSignals() {
const signals = [
'volume-added',
'volume-removed',
'volume-changed',
'mount-added',
'mount-removed',
'mount-changed',
'drive-connected',
'drive-disconnected',
'drive-changed',
];
this._volumeMonitorSignals = [];
let func = this._updateMounts.bind(this);
for (let i = 0; i < signals.length; i++) {
let id = this._volumeMonitor.connect(signals[i], func);
this._volumeMonitorSignals.push(id);
}
}
destroy() { destroy() {
if (this._settings) this._settings?.disconnectObject(this);
this._settings.disconnect(this._showDesktopIconsChangedId);
this._settings = null; this._settings = null;
for (let i = 0; i < this._volumeMonitorSignals.length; i++) this._volumeMonitor.disconnectObject(this);
this._volumeMonitor.disconnect(this._volumeMonitorSignals[i]);
if (this._monitor) if (this._monitor)
this._monitor.cancel(); this._monitor.cancel();
@@ -546,4 +533,4 @@ var PlacesManager = class extends EventEmitter {
get(kind) { get(kind) {
return this._places[kind]; return this._places[kind];
} }
}; }

View File

@@ -1,4 +1,3 @@
/* exported enable disable */
/* Screenshot Window Sizer for Gnome Shell /* Screenshot Window Sizer for Gnome Shell
* *
* Copyright (c) 2013 Owen Taylor <otaylor@redhat.com> * Copyright (c) 2013 Owen Taylor <otaylor@redhat.com>
@@ -19,153 +18,151 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
const {Clutter, Meta, Shell, St} = imports.gi; import Clutter from 'gi://Clutter';
import Meta from 'gi://Meta';
import Shell from 'gi://Shell';
import St from 'gi://St';
const ExtensionUtils = imports.misc.extensionUtils; import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
const Main = imports.ui.main;
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
const MESSAGE_FADE_TIME = 2000; const MESSAGE_FADE_TIME = 2000;
let text; export default class ScreenshotWindowSizerExtension extends Extension {
SIZES = [
[624, 351],
[800, 450],
[1024, 576],
[1200, 675],
[1600, 900],
[360, 654], // Phone portrait maximized
[720, 360], // Phone landscape fullscreen
];
/** */ _flashMessage(message) {
function hideMessage() { if (!this._text) {
text.destroy(); this._text = new St.Label({style_class: 'screenshot-sizer-message'});
text = null; Main.uiGroup.add_actor(this._text);
}
/**
* @param {string} message - the message to flash
*/
function flashMessage(message) {
if (!text) {
text = new St.Label({style_class: 'screenshot-sizer-message'});
Main.uiGroup.add_actor(text);
}
text.remove_all_transitions();
text.text = message;
text.opacity = 255;
let monitor = Main.layoutManager.primaryMonitor;
text.set_position(
monitor.x + Math.floor(monitor.width / 2 - text.width / 2),
monitor.y + Math.floor(monitor.height / 2 - text.height / 2));
text.ease({
opacity: 0,
duration: MESSAGE_FADE_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: hideMessage,
});
}
let SIZES = [
[624, 351],
[800, 450],
[1024, 576],
[1200, 675],
[1600, 900],
[360, 654], // Phone portrait maximized
[720, 360], // Phone landscape fullscreen
];
/**
* @param {Meta.Display} display - the display
* @param {Meta.Window=} window - for per-window bindings, the window
* @param {Meta.KeyBinding} binding - the key binding
*/
function cycleScreenshotSizes(display, window, binding) {
// Probably this isn't useful with 5 sizes, but you can decrease instead
// of increase by holding down shift.
let modifiers = binding.get_modifiers();
let backwards = (modifiers & Meta.VirtualModifier.SHIFT_MASK) !== 0;
// Unmaximize first
if (window.get_maximized() !== 0)
window.unmaximize(Meta.MaximizeFlags.BOTH);
let workArea = window.get_work_area_current_monitor();
let outerRect = window.get_frame_rect();
// Double both axes if on a hidpi display
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let scaledSizes = SIZES.map(size => size.map(wh => wh * scaleFactor))
.filter(([w, h]) => w <= workArea.width && h <= workArea.height);
// Find the nearest 16:9 size for the current window size
let nearestIndex;
let nearestError;
for (let i = 0; i < scaledSizes.length; i++) {
let [width, height] = scaledSizes[i];
// get the best initial window size
let error = Math.abs(width - outerRect.width) + Math.abs(height - outerRect.height);
if (nearestIndex === undefined || error < nearestError) {
nearestIndex = i;
nearestError = error;
} }
this._text.remove_all_transitions();
this._text.text = message;
this._text.opacity = 255;
const monitor = Main.layoutManager.primaryMonitor;
this._text.set_position(
monitor.x + Math.floor(monitor.width / 2 - this._text.width / 2),
monitor.y + Math.floor(monitor.height / 2 - this._text.height / 2));
this._text.ease({
opacity: 0,
duration: MESSAGE_FADE_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this._hideMessage(),
});
} }
// get the next size up or down from ideal _hideMessage() {
let newIndex = (nearestIndex + (backwards ? -1 : 1)) % scaledSizes.length; this._text.destroy();
let [newWidth, newHeight] = scaledSizes[newIndex]; delete this._text;
}
// Push the window onscreen if it would be resized offscreen /**
let newX = outerRect.x; * @param {Meta.Display} display - the display
let newY = outerRect.y; * @param {Meta.Window=} window - for per-window bindings, the window
if (newX + newWidth > workArea.x + workArea.width) * @param {Meta.KeyBinding} binding - the key binding
newX = Math.max(workArea.x + workArea.width - newWidth); */
if (newY + newHeight > workArea.y + workArea.height) _cycleScreenshotSizes(display, window, binding) {
newY = Math.max(workArea.y + workArea.height - newHeight); // Probably this isn't useful with 5 sizes, but you can decrease instead
// of increase by holding down shift.
let modifiers = binding.get_modifiers();
let backwards = (modifiers & Meta.VirtualModifier.SHIFT_MASK) !== 0;
const id = window.connect('size-changed', () => { // Unmaximize first
window.disconnect(id); if (window.get_maximized() !== 0)
_notifySizeChange(window); window.unmaximize(Meta.MaximizeFlags.BOTH);
});
window.move_resize_frame(true, newX, newY, newWidth, newHeight); let workArea = window.get_work_area_current_monitor();
} let outerRect = window.get_frame_rect();
/** // Double both axes if on a hidpi display
* @param {Meta.Window} window - the window whose size changed let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
*/ let scaledSizes = this.SIZES.map(size => size.map(wh => wh * scaleFactor))
function _notifySizeChange(window) { .filter(([w, h]) => w <= workArea.width && h <= workArea.height);
const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
let newOuterRect = window.get_frame_rect(); // Find the nearest 16:9 size for the current window size
let message = '%d×%d'.format( let nearestIndex;
newOuterRect.width / scaleFactor, let nearestError;
newOuterRect.height / scaleFactor);
for (let i = 0; i < scaledSizes.length; i++) {
// The new size might have been constrained by geometry hints (e.g. for let [width, height] = scaledSizes[i];
// a terminal) - in that case, include the actual ratio to the message
// we flash // get the best initial window size
let actualNumerator = 9 * newOuterRect.width / newOuterRect.height; let error = Math.abs(width - outerRect.width) + Math.abs(height - outerRect.height);
if (Math.abs(actualNumerator - 16) > 0.01) if (nearestIndex === undefined || error < nearestError) {
message += ' (%.2f:9)'.format(actualNumerator); nearestIndex = i;
nearestError = error;
flashMessage(message); }
} }
/** */ // get the next size up or down from ideal
function enable() { let newIndex = (nearestIndex + (backwards ? -1 : 1)) % scaledSizes.length;
Main.wm.addKeybinding( let [newWidth, newHeight] = scaledSizes[newIndex];
'cycle-screenshot-sizes',
ExtensionUtils.getSettings(), // Push the window onscreen if it would be resized offscreen
Meta.KeyBindingFlags.PER_WINDOW, let newX = outerRect.x;
Shell.ActionMode.NORMAL, let newY = outerRect.y;
cycleScreenshotSizes); if (newX + newWidth > workArea.x + workArea.width)
Main.wm.addKeybinding( newX = Math.max(workArea.x + workArea.width - newWidth);
'cycle-screenshot-sizes-backward', if (newY + newHeight > workArea.y + workArea.height)
ExtensionUtils.getSettings(), newY = Math.max(workArea.y + workArea.height - newHeight);
Meta.KeyBindingFlags.PER_WINDOW | Meta.KeyBindingFlags.IS_REVERSED,
Shell.ActionMode.NORMAL, const id = window.connect('size-changed', () => {
cycleScreenshotSizes); window.disconnect(id);
} this._notifySizeChange(window);
});
/** */ window.move_resize_frame(true, newX, newY, newWidth, newHeight);
function disable() { }
Main.wm.removeKeybinding('cycle-screenshot-sizes');
Main.wm.removeKeybinding('cycle-screenshot-sizes-backward'); /**
* @param {Meta.Window} window - the window whose size changed
*/
_notifySizeChange(window) {
const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
let newOuterRect = window.get_frame_rect();
let message = '%d×%d'.format(
newOuterRect.width / scaleFactor,
newOuterRect.height / scaleFactor);
// The new size might have been constrained by geometry hints (e.g. for
// a terminal) - in that case, include the actual ratio to the message
// we flash
let actualNumerator = 9 * newOuterRect.width / newOuterRect.height;
if (Math.abs(actualNumerator - 16) > 0.01)
message += ' (%.2f:9)'.format(actualNumerator);
this._flashMessage(message);
}
enable() {
Main.wm.addKeybinding(
'cycle-screenshot-sizes',
this.getSettings(),
Meta.KeyBindingFlags.PER_WINDOW,
Shell.ActionMode.NORMAL,
this._cycleScreenshotSizes.bind(this));
Main.wm.addKeybinding(
'cycle-screenshot-sizes-backward',
this.getSettings(),
Meta.KeyBindingFlags.PER_WINDOW | Meta.KeyBindingFlags.IS_REVERSED,
Shell.ActionMode.NORMAL,
this._cycleScreenshotSizes.bind(this));
}
disable() {
Main.wm.removeKeybinding('cycle-screenshot-sizes');
Main.wm.removeKeybinding('cycle-screenshot-sizes-backward');
}
} }

View File

@@ -1,20 +1,19 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- // -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
// Load shell theme from ~/.local/share/themes/name/gnome-shell // Load shell theme from ~/.local/share/themes/name/gnome-shell
/* exported init */
const {Gio} = imports.gi; import Gio from 'gi://Gio';
const ExtensionUtils = imports.misc.extensionUtils; import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
const Main = imports.ui.main;
const Me = ExtensionUtils.getCurrentExtension(); import * as Main from 'resource:///org/gnome/shell/ui/main.js';
const Util = Me.imports.util;
import {getThemeDirs, getModeThemeDirs} from './util.js';
const SETTINGS_KEY = 'name'; const SETTINGS_KEY = 'name';
class ThemeManager { export default class ThemeManager extends Extension {
enable() { enable() {
this._settings = ExtensionUtils.getSettings(); this._settings = this.getSettings();
this._settings.connect(`changed::${SETTINGS_KEY}`, this._changeTheme.bind(this)); this._settings.connect(`changed::${SETTINGS_KEY}`, this._changeTheme.bind(this));
this._changeTheme(); this._changeTheme();
} }
@@ -32,10 +31,10 @@ class ThemeManager {
let themeName = this._settings.get_string(SETTINGS_KEY); let themeName = this._settings.get_string(SETTINGS_KEY);
if (themeName) { if (themeName) {
const stylesheetPaths = Util.getThemeDirs() const stylesheetPaths = getThemeDirs()
.map(dir => `${dir}/${themeName}/gnome-shell/gnome-shell.css`); .map(dir => `${dir}/${themeName}/gnome-shell/gnome-shell.css`);
stylesheetPaths.push(...Util.getModeThemeDirs() stylesheetPaths.push(...getModeThemeDirs()
.map(dir => `${dir}/${themeName}.css`)); .map(dir => `${dir}/${themeName}.css`));
stylesheet = stylesheetPaths.find(path => { stylesheet = stylesheetPaths.find(path => {
@@ -45,17 +44,10 @@ class ThemeManager {
} }
if (stylesheet) if (stylesheet)
global.log(`loading user theme: ${stylesheet}`); log(`loading user theme: ${stylesheet}`);
else else
global.log('loading default theme (Adwaita)'); log('loading default theme (Adwaita)');
Main.setThemeStylesheet(stylesheet); Main.setThemeStylesheet(stylesheet);
Main.loadTheme(); Main.loadTheme();
} }
} }
/**
* @returns {ThemeManager} - the extension state object
*/
function init() {
return new ThemeManager();
}

View File

@@ -1,15 +1,17 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- // -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
/* exported init buildPrefsWidget */
// we use async/await here to not block the mainloop, not to parallelize // we use async/await here to not block the mainloop, not to parallelize
/* eslint-disable no-await-in-loop */ /* eslint-disable no-await-in-loop */
const {Adw, Gio, GLib, GObject, Gtk} = imports.gi; import Adw from 'gi://Adw';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Gtk from 'gi://Gtk';
const ExtensionUtils = imports.misc.extensionUtils; import {ExtensionPreferences} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
const Me = ExtensionUtils.getCurrentExtension(); import {getThemeDirs, getModeThemeDirs} from './util.js';
const Util = Me.imports.util;
Gio._promisify(Gio.File.prototype, 'enumerate_children_async'); Gio._promisify(Gio.File.prototype, 'enumerate_children_async');
Gio._promisify(Gio.File.prototype, 'query_info_async'); Gio._promisify(Gio.File.prototype, 'query_info_async');
@@ -20,13 +22,13 @@ class UserThemePrefsWidget extends Adw.PreferencesGroup {
GObject.registerClass(this); GObject.registerClass(this);
} }
constructor() { constructor(settings) {
super({title: 'Themes'}); super({title: 'Themes'});
this._actionGroup = new Gio.SimpleActionGroup(); this._actionGroup = new Gio.SimpleActionGroup();
this.insert_action_group('theme', this._actionGroup); this.insert_action_group('theme', this._actionGroup);
this._settings = ExtensionUtils.getSettings(); this._settings = settings;
this._actionGroup.add_action( this._actionGroup.add_action(
this._settings.create_action('name')); this._settings.create_action('name'));
@@ -39,7 +41,7 @@ class UserThemePrefsWidget extends Adw.PreferencesGroup {
} }
async _collectThemes() { async _collectThemes() {
for (const dirName of Util.getThemeDirs()) { for (const dirName of getThemeDirs()) {
const dir = Gio.File.new_for_path(dirName); const dir = Gio.File.new_for_path(dirName);
for (const name of await this._enumerateDir(dir)) { for (const name of await this._enumerateDir(dir)) {
if (this._rows.has(name)) if (this._rows.has(name))
@@ -60,7 +62,7 @@ class UserThemePrefsWidget extends Adw.PreferencesGroup {
} }
} }
for (const dirName of Util.getModeThemeDirs()) { for (const dirName of getModeThemeDirs()) {
const dir = Gio.File.new_for_path(dirName); const dir = Gio.File.new_for_path(dirName);
for (const filename of await this._enumerateDir(dir)) { for (const filename of await this._enumerateDir(dir)) {
if (!filename.endsWith('.css')) if (!filename.endsWith('.css'))
@@ -125,13 +127,8 @@ class ThemeRow extends Adw.ActionRow {
} }
} }
/** */ export default class UserThemePrefs extends ExtensionPreferences {
function init() { getPreferencesWidget() {
} return new UserThemePrefsWidget(this.getSettings());
}
/**
* @returns {Gtk.Widget} - the prefs widget
*/
function buildPrefsWidget() {
return new UserThemePrefsWidget();
} }

View File

@@ -1,12 +1,11 @@
/* exported getThemeDirs getModeThemeDirs */ import GLib from 'gi://GLib';
const {GLib} = imports.gi;
const fn = (...args) => GLib.build_filenamev(args); const fn = (...args) => GLib.build_filenamev(args);
/** /**
* @returns {string[]} - an ordered list of theme directories * @returns {string[]} - an ordered list of theme directories
*/ */
function getThemeDirs() { export function getThemeDirs() {
return [ return [
fn(GLib.get_home_dir(), '.themes'), fn(GLib.get_home_dir(), '.themes'),
fn(GLib.get_user_data_dir(), 'themes'), fn(GLib.get_user_data_dir(), 'themes'),
@@ -17,7 +16,7 @@ function getThemeDirs() {
/** /**
* @returns {string[]} - an ordered list of mode theme directories * @returns {string[]} - an ordered list of mode theme directories
*/ */
function getModeThemeDirs() { export function getModeThemeDirs() {
return GLib.get_system_data_dirs() return GLib.get_system_data_dirs()
.map(dir => fn(dir, 'gnome-shell', 'theme')); .map(dir => fn(dir, 'gnome-shell', 'theme'));
} }

View File

@@ -1,21 +1,28 @@
/* exported init */ import Clutter from 'gi://Clutter';
const {Clutter, Gio, GLib, GObject, Gtk, Meta, Shell, St} = imports.gi; import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Gtk from 'gi://Gtk';
import Meta from 'gi://Meta';
import Shell from 'gi://Shell';
import St from 'gi://St';
const DND = imports.ui.dnd; import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const PopupMenu = imports.ui.popupMenu;
const Me = ExtensionUtils.getCurrentExtension(); import * as DND from 'resource:///org/gnome/shell/ui/dnd.js';
const {WindowPicker, WindowPickerToggle} = Me.imports.windowPicker; import * as Main from 'resource:///org/gnome/shell/ui/main.js';
const {WorkspaceIndicator} = Me.imports.workspaceIndicator; import * as Overview from 'resource:///org/gnome/shell/ui/overview.js';
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
const _ = ExtensionUtils.gettext; import {WindowPicker, WindowPickerToggle} from './windowPicker.js';
import {WorkspaceIndicator} from './workspaceIndicator.js';
const ICON_TEXTURE_SIZE = 24; const ICON_TEXTURE_SIZE = 24;
const DND_ACTIVATE_TIMEOUT = 500; const DND_ACTIVATE_TIMEOUT = 500;
const TOOLTIP_OFFSET = 6;
const TOOLTIP_ANIMATION_TIME = 150;
const GroupingMode = { const GroupingMode = {
NEVER: 0, NEVER: 0,
AUTO: 1, AUTO: 1,
@@ -48,10 +55,6 @@ class WindowContextMenu extends PopupMenu.PopupMenu {
}); });
this.addMenuItem(this._minimizeItem); this.addMenuItem(this._minimizeItem);
this._notifyMinimizedId = this._metaWindow.connect(
'notify::minimized', this._updateMinimizeItem.bind(this));
this._updateMinimizeItem();
this._maximizeItem = new PopupMenu.PopupMenuItem(''); this._maximizeItem = new PopupMenu.PopupMenuItem('');
this._maximizeItem.connect('activate', () => { this._maximizeItem.connect('activate', () => {
if (this._metaWindow.get_maximized() === Meta.MaximizeFlags.BOTH) if (this._metaWindow.get_maximized() === Meta.MaximizeFlags.BOTH)
@@ -61,21 +64,20 @@ class WindowContextMenu extends PopupMenu.PopupMenu {
}); });
this.addMenuItem(this._maximizeItem); this.addMenuItem(this._maximizeItem);
this._notifyMaximizedHId = this._metaWindow.connect(
'notify::maximized-horizontally',
this._updateMaximizeItem.bind(this));
this._notifyMaximizedVId = this._metaWindow.connect(
'notify::maximized-vertically',
this._updateMaximizeItem.bind(this));
this._updateMaximizeItem();
this._closeItem = new PopupMenu.PopupMenuItem(_('Close')); this._closeItem = new PopupMenu.PopupMenuItem(_('Close'));
this._closeItem.connect('activate', () => { this._closeItem.connect('activate', () => {
this._metaWindow.delete(global.get_current_time()); this._metaWindow.delete(global.get_current_time());
}); });
this.addMenuItem(this._closeItem); this.addMenuItem(this._closeItem);
this.actor.connect('destroy', this._onDestroy.bind(this)); this._metaWindow.connectObject(
'notify::minimized', this._updateMinimizeItem.bind(this),
'notify::maximized-horizontally', this._updateMaximizeItem.bind(this),
'notify::maximized-vertically', this._updateMaximizeItem.bind(this),
this);
this._updateMinimizeItem();
this._updateMaximizeItem();
this.connect('open-state-changed', () => { this.connect('open-state-changed', () => {
if (!this.isOpen) if (!this.isOpen)
@@ -98,12 +100,6 @@ class WindowContextMenu extends PopupMenu.PopupMenu {
this._maximizeItem.label.text = maximized this._maximizeItem.label.text = maximized
? _('Unmaximize') : _('Maximize'); ? _('Unmaximize') : _('Maximize');
} }
_onDestroy() {
this._metaWindow.disconnect(this._notifyMinimizedId);
this._metaWindow.disconnect(this._notifyMaximizedHId);
this._metaWindow.disconnect(this._notifyMaximizedVId);
}
} }
class WindowTitle extends St.BoxLayout { class WindowTitle extends St.BoxLayout {
@@ -127,20 +123,19 @@ class WindowTitle extends St.BoxLayout {
this.add(this.label_actor); this.add(this.label_actor);
this._textureCache = St.TextureCache.get_default(); this._textureCache = St.TextureCache.get_default();
this._iconThemeChangedId = this._textureCache.connect( this._textureCache.connectObject('icon-theme-changed',
'icon-theme-changed', this._updateIcon.bind(this)); () => this._updateIcon(), this);
this._notifyWmClass = this._metaWindow.connect_after(
'notify::wm-class', this._updateIcon.bind(this)); this._metaWindow.connectObject(
this._notifyAppId = this._metaWindow.connect_after( 'notify::wm-class',
'notify::gtk-application-id', this._updateIcon.bind(this)); () => this._updateIcon(), GObject.ConnectFlags.AFTER,
'notify::gtk-application-id',
() => this._updateIcon(), GObject.ConnectFlags.AFTER,
'notify::title', () => this._updateTitle(),
'notify::minimized', () => this._minimizedChanged(),
this);
this._updateIcon(); this._updateIcon();
this.connect('destroy', this._onDestroy.bind(this));
this._notifyTitleId = this._metaWindow.connect(
'notify::title', this._updateTitle.bind(this));
this._notifyMinimizedId = this._metaWindow.connect(
'notify::minimized', this._minimizedChanged.bind(this));
this._minimizedChanged(); this._minimizedChanged();
} }
@@ -170,14 +165,6 @@ class WindowTitle extends St.BoxLayout {
}); });
} }
} }
_onDestroy() {
this._textureCache.disconnect(this._iconThemeChangedId);
this._metaWindow.disconnect(this._notifyTitleId);
this._metaWindow.disconnect(this._notifyMinimizedId);
this._metaWindow.disconnect(this._notifyWmClass);
this._metaWindow.disconnect(this._notifyAppId);
}
} }
class BaseButton extends St.Button { class BaseButton extends St.Button {
@@ -213,17 +200,23 @@ class BaseButton extends St.Button {
this._contextMenuManager = new PopupMenu.PopupMenuManager(this); this._contextMenuManager = new PopupMenu.PopupMenuManager(this);
this._switchWorkspaceId = global.window_manager.connect( global.window_manager.connectObject('switch-workspace',
'switch-workspace', this._updateVisibility.bind(this)); () => this._updateVisibility(), this);
if (this._perMonitor) { if (this._perMonitor) {
this._windowEnteredMonitorId = global.display.connect( global.display.connectObject(
'window-entered-monitor', 'window-entered-monitor',
this._windowEnteredOrLeftMonitor.bind(this)); this._windowEnteredOrLeftMonitor.bind(this),
this._windowLeftMonitorId = global.display.connect(
'window-left-monitor', 'window-left-monitor',
this._windowEnteredOrLeftMonitor.bind(this)); this._windowEnteredOrLeftMonitor.bind(this),
this);
} }
this._tooltip = new Tooltip(this, {
style_class: 'dash-label',
visible: false,
});
Main.uiGroup.add_child(this._tooltip);
} }
get active() { get active() {
@@ -325,9 +318,11 @@ class BaseButton extends St.Button {
if (isOpen) if (isOpen)
return; return;
const extension = Extension.lookupByURL(import.meta.url);
let [x, y] = global.get_pointer(); let [x, y] = global.get_pointer();
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y); let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
if (Me.stateObj.someWindowListContains(actor)) if (extension.someWindowListContains(actor))
actor.sync_hover(); actor.sync_hover();
} }
@@ -382,15 +377,7 @@ class BaseButton extends St.Button {
} }
_onDestroy() { _onDestroy() {
global.window_manager.disconnect(this._switchWorkspaceId); this._tooltip.destroy();
if (this._windowEnteredMonitorId)
global.display.disconnect(this._windowEnteredMonitorId);
this._windowEnteredMonitorId = 0;
if (this._windowLeftMonitorId)
global.display.disconnect(this._windowLeftMonitorId);
this._windowLeftMonitorId = 0;
} }
} }
@@ -403,9 +390,11 @@ class WindowButton extends BaseButton {
super(perMonitor, monitorIndex); super(perMonitor, monitorIndex);
this.metaWindow = metaWindow; this.metaWindow = metaWindow;
this._skipTaskbarId = metaWindow.connect('notify::skip-taskbar', () => { metaWindow.connectObject(
this._updateVisibility(); 'notify::skip-taskbar', () => this._updateVisibility(),
}); 'workspace-changed', () => this._updateVisibility(),
this);
this._updateVisibility(); this._updateVisibility();
this._windowTitle = new WindowTitle(this.metaWindow); this._windowTitle = new WindowTitle(this.metaWindow);
@@ -419,11 +408,8 @@ class WindowButton extends BaseButton {
this._contextMenuManager.addMenu(this._contextMenu); this._contextMenuManager.addMenu(this._contextMenu);
Main.uiGroup.add_actor(this._contextMenu.actor); Main.uiGroup.add_actor(this._contextMenu.actor);
this._workspaceChangedId = this.metaWindow.connect( global.display.connectObject('notify::focus-window',
'workspace-changed', this._updateVisibility.bind(this)); () => this._updateStyle(), this);
this._notifyFocusId = global.display.connect(
'notify::focus-window', this._updateStyle.bind(this));
this._updateStyle(); this._updateStyle();
} }
@@ -467,9 +453,6 @@ class WindowButton extends BaseButton {
_onDestroy() { _onDestroy() {
super._onDestroy(); super._onDestroy();
this.metaWindow.disconnect(this._skipTaskbarId);
this.metaWindow.disconnect(this._workspaceChangedId);
global.display.disconnect(this._notifyFocusId);
this._contextMenu.destroy(); this._contextMenu.destroy();
} }
} }
@@ -586,18 +569,17 @@ class AppButton extends BaseButton {
Main.uiGroup.add_actor(this._appContextMenu.actor); Main.uiGroup.add_actor(this._appContextMenu.actor);
this._textureCache = St.TextureCache.get_default(); this._textureCache = St.TextureCache.get_default();
this._iconThemeChangedId = this._textureCache.connectObject('icon-theme-changed', () => {
this._textureCache.connect('icon-theme-changed', () => { this._icon.child = app.create_icon_texture(ICON_TEXTURE_SIZE);
this._icon.child = app.create_icon_texture(ICON_TEXTURE_SIZE); }, this);
});
this._windowsChangedId = this.app.connect( this.app.connectObject('windows-changed',
'windows-changed', this._windowsChanged.bind(this)); () => this._windowsChanged(), this);
this._windowsChanged(); this._windowsChanged();
this._windowTracker = Shell.WindowTracker.get_default(); this._windowTracker = Shell.WindowTracker.get_default();
this._notifyFocusId = this._windowTracker.connect( this._windowTracker.connectObject('notify::focus-app',
'notify::focus-app', this._updateStyle.bind(this)); () => this._updateStyle(), this);
this._updateStyle(); this._updateStyle();
} }
@@ -717,9 +699,6 @@ class AppButton extends BaseButton {
_onDestroy() { _onDestroy() {
super._onDestroy(); super._onDestroy();
this._textureCache.disconnect(this._iconThemeChangedId);
this._windowTracker.disconnect(this._notifyFocusId);
this.app.disconnect(this._windowsChangedId);
this._menu.destroy(); this._menu.destroy();
} }
} }
@@ -729,7 +708,7 @@ class WindowList extends St.Widget {
GObject.registerClass(this); GObject.registerClass(this);
} }
constructor(perMonitor, monitor) { constructor(perMonitor, monitor, settings) {
super({ super({
name: 'panel', name: 'panel',
style_class: 'bottom-panel solid', style_class: 'bottom-panel solid',
@@ -776,12 +755,12 @@ class WindowList extends St.Widget {
indicatorsBox.add_child(this._workspaceIndicator.container); indicatorsBox.add_child(this._workspaceIndicator.container);
this._mutterSettings = new Gio.Settings({schema_id: 'org.gnome.mutter'}); this._mutterSettings = new Gio.Settings({schema_id: 'org.gnome.mutter'});
this._workspacesOnlyOnPrimaryChangedId = this._mutterSettings.connect( this._mutterSettings.connectObject(
'changed::workspaces-only-on-primary', 'changed::workspaces-only-on-primary',
this._updateWorkspaceIndicatorVisibility.bind(this)); () => this._updateWorkspaceIndicatorVisibility(),
this._dynamicWorkspacesChangedId = this._mutterSettings.connect(
'changed::dynamic-workspaces', 'changed::dynamic-workspaces',
this._updateWorkspaceIndicatorVisibility.bind(this)); () => this._updateWorkspaceIndicatorVisibility(),
this);
this._updateWorkspaceIndicatorVisibility(); this._updateWorkspaceIndicatorVisibility();
this._menuManager = new PopupMenu.PopupMenuManager(this); this._menuManager = new PopupMenu.PopupMenuManager(this);
@@ -799,59 +778,58 @@ class WindowList extends St.Widget {
this._updatePosition(); this._updatePosition();
this._appSystem = Shell.AppSystem.get_default(); this._appSystem = Shell.AppSystem.get_default();
this._appStateChangedId = this._appSystem.connect( this._appSystem.connectObject('app-state-changed',
'app-state-changed', this._onAppStateChanged.bind(this)); this._onAppStateChanged.bind(this), this);
// Hack: OSK gesture is tied to visibility, piggy-back on that // Hack: OSK gesture is tied to visibility, piggy-back on that
this._keyboardVisiblechangedId = Main.keyboard._bottomDragAction.connectObject('notify::enabled',
Main.keyboard._bottomDragAction.connect('notify::enabled', action => {
action => { const visible = !action.enabled;
const visible = !action.enabled; if (visible) {
if (visible) { Main.uiGroup.set_child_above_sibling(
Main.uiGroup.set_child_above_sibling( this, Main.layoutManager.keyboardBox);
this, Main.layoutManager.keyboardBox); } else {
} else { Main.uiGroup.set_child_above_sibling(
Main.uiGroup.set_child_above_sibling( this, Main.layoutManager.panelBox);
this, Main.layoutManager.panelBox); }
} this._updateKeyboardAnchor();
this._updateKeyboardAnchor(); }, this);
});
let workspaceManager = global.workspace_manager; let workspaceManager = global.workspace_manager;
this._nWorkspacesChangedId = workspaceManager.connect( workspaceManager.connectObject('notify::n-workspaces',
'notify::n-workspaces', this._updateWorkspaceIndicatorVisibility.bind(this)); () => this._updateWorkspaceIndicatorVisibility(), this);
this._updateWorkspaceIndicatorVisibility(); this._updateWorkspaceIndicatorVisibility();
this._switchWorkspaceId = global.window_manager.connect( global.window_manager.connectObject('switch-workspace',
'switch-workspace', this._checkGrouping.bind(this)); () => this._checkGrouping(), this);
this._overviewShowingId = Main.overview.connect('showing', () => { Main.overview.connectObject(
this.hide(); 'showing', () => {
this._updateKeyboardAnchor(); this.hide();
});
this._overviewHidingId = Main.overview.connect('hidden', () => {
this.visible = !this._monitor.inFullscreen;
this._updateKeyboardAnchor();
});
this._fullscreenChangedId =
global.display.connect('in-fullscreen-changed', () => {
// Work-around for initial change from unknown to !fullscreen
if (Main.overview.visible)
this.hide();
this._updateKeyboardAnchor(); this._updateKeyboardAnchor();
}); },
'hidden', () => {
this.visible = !this._monitor.inFullscreen;
this._updateKeyboardAnchor();
}, this);
global.display.connectObject('in-fullscreen-changed', () => {
// Work-around for initial change from unknown to !fullscreen
if (Main.overview.visible)
this.hide();
this._updateKeyboardAnchor();
}, this);
this._windowSignals = new Map(); this._windowSignals = new Map();
this._windowCreatedId = global.display.connect( this._windowCreatedId = global.display.connect(
'window-created', (dsp, win) => this._addWindow(win)); 'window-created', (dsp, win) => this._addWindow(win));
this._dragBeginId = Main.xdndHandler.connect('drag-begin', Main.xdndHandler.connectObject(
this._monitorDrag.bind(this)); 'drag-begin', () => this._monitorDrag(),
this._dragEndId = Main.xdndHandler.connect('drag-end', 'drag-end', () => this._stopMonitoringDrag(),
this._stopMonitoringDrag.bind(this)); this);
this._dragMonitor = { this._dragMonitor = {
dragMotion: this._onDragMotion.bind(this), dragMotion: this._onDragMotion.bind(this),
}; };
@@ -859,7 +837,7 @@ class WindowList extends St.Widget {
this._dndTimeoutId = 0; this._dndTimeoutId = 0;
this._dndWindow = null; this._dndWindow = null;
this._settings = ExtensionUtils.getSettings(); this._settings = settings;
this._settings.connect('changed::grouping-mode', this._settings.connect('changed::grouping-mode',
() => this._groupingModeChanged()); () => this._groupingModeChanged());
this._grouped = undefined; this._grouped = undefined;
@@ -900,7 +878,8 @@ class WindowList extends St.Widget {
} }
_updateWindowListVisibility() { _updateWindowListVisibility() {
let visible = !Main.windowPicker.visible; const {windowPicker} = Extension.lookupByURL(import.meta.url);
const visible = !windowPicker.visible;
this._windowList.ease({ this._windowList.ease({
opacity: visible ? 255 : 0, opacity: visible ? 255 : 0,
@@ -1098,37 +1077,14 @@ class WindowList extends St.Widget {
} }
_onDestroy() { _onDestroy() {
this._mutterSettings.disconnect(this._workspacesOnlyOnPrimaryChangedId);
this._mutterSettings.disconnect(this._dynamicWorkspacesChangedId);
this._workspaceIndicator.destroy(); this._workspaceIndicator.destroy();
Main.ctrlAltTabManager.removeGroup(this); Main.ctrlAltTabManager.removeGroup(this);
this._appSystem.disconnect(this._appStateChangedId);
this._appStateChangedId = 0;
Main.keyboard._bottomDragAction.disconnect(this._keyboardVisiblechangedId);
this._keyboardVisiblechangedId = 0;
global.workspace_manager.disconnect(this._nWorkspacesChangedId);
this._nWorkspacesChangedId = 0;
global.window_manager.disconnect(this._switchWorkspaceId);
this._switchWorkspaceId = 0;
this._windowSignals.forEach((id, win) => win.disconnect(id)); this._windowSignals.forEach((id, win) => win.disconnect(id));
this._windowSignals.clear(); this._windowSignals.clear();
Main.overview.disconnect(this._overviewShowingId);
Main.overview.disconnect(this._overviewHidingId);
global.display.disconnect(this._fullscreenChangedId);
global.display.disconnect(this._windowCreatedId);
this._stopMonitoringDrag(); this._stopMonitoringDrag();
Main.xdndHandler.disconnect(this._dragBeginId);
Main.xdndHandler.disconnect(this._dragEndId);
this._settings.run_dispose(); this._settings.run_dispose();
@@ -1138,9 +1094,9 @@ class WindowList extends St.Widget {
} }
} }
class Extension { export default class WindowListExtension extends Extension {
constructor() { constructor(metadata) {
ExtensionUtils.initTranslations(); super(metadata);
this._windowLists = null; this._windowLists = null;
this._hideOverviewOrig = Main.overview.hide; this._hideOverviewOrig = Main.overview.hide;
@@ -1149,17 +1105,17 @@ class Extension {
enable() { enable() {
this._windowLists = []; this._windowLists = [];
this._settings = ExtensionUtils.getSettings(); this._settings = this.getSettings();
this._showOnAllMonitorsChangedId = this._settings.connect( this._settings.connectObject('changed::show-on-all-monitors',
'changed::show-on-all-monitors', this._buildWindowLists.bind(this)); () => this._buildWindowLists(), this);
this._monitorsChangedId = Main.layoutManager.connect( Main.layoutManager.connectObject('monitors-changed',
'monitors-changed', this._buildWindowLists.bind(this)); () => this._buildWindowLists(), this);
Main.windowPicker = new WindowPicker(); this.windowPicker = new WindowPicker();
Main.overview.hide = () => { Main.overview.hide = () => {
Main.windowPicker.close(); this.windowPicker.close();
this._hideOverviewOrig.call(Main.overview); this._hideOverviewOrig.call(Main.overview);
}; };
@@ -1174,7 +1130,7 @@ class Extension {
Main.layoutManager.monitors.forEach(monitor => { Main.layoutManager.monitors.forEach(monitor => {
if (showOnAllMonitors || monitor === Main.layoutManager.primaryMonitor) if (showOnAllMonitors || monitor === Main.layoutManager.primaryMonitor)
this._windowLists.push(new WindowList(showOnAllMonitors, monitor)); this._windowLists.push(new WindowList(showOnAllMonitors, monitor, this.getSettings()));
}); });
} }
@@ -1182,11 +1138,8 @@ class Extension {
if (!this._windowLists) if (!this._windowLists)
return; return;
this._settings.disconnect(this._showOnAllMonitorsChangedId); this._settings.disconnectObject(this);
this._showOnAllMonitorsChangedId = 0; Main.layoutManager.disconnectObject(this);
Main.layoutManager.disconnect(this._monitorsChangedId);
this._monitorsChangedId = 0;
this._windowLists.forEach(windowList => { this._windowLists.forEach(windowList => {
windowList.hide(); windowList.hide();
@@ -1194,8 +1147,8 @@ class Extension {
}); });
this._windowLists = null; this._windowLists = null;
Main.windowPicker.destroy(); this.windowPicker.destroy();
delete Main.windowPicker; delete this.windowPicker;
Main.overview.hide = this._hideOverviewOrig; Main.overview.hide = this._hideOverviewOrig;
} }
@@ -1205,9 +1158,66 @@ class Extension {
} }
} }
/** class Tooltip extends St.Label {
* @returns {Extension} - the extension's state object static {
*/ GObject.registerClass(this);
function init() { }
return new Extension();
constructor(widget, params) {
super(params);
this._widget = widget;
this._widget.connect('notify::hover', () => {
if (this._widget.hover)
this.open();
else
this.close();
});
}
open() {
const buttonTitleWidget = this._widget.label_actor;
const [, , preferredTitleWidth] = buttonTitleWidget.get_preferred_size();
const maxTitleWidth = buttonTitleWidget.allocation.get_width();
const isTitleFullyShown = preferredTitleWidth <= maxTitleWidth;
if (isTitleFullyShown)
return;
this.set({
text: this._widget.label_actor.get_text(),
visible: true,
opacity: 0,
});
const [stageX, stageY] = this._widget.get_transformed_position();
const thumbWidth = this._widget.allocation.get_width();
const tipWidth = this.width;
const tipHeight = this.height;
const xOffset = Math.floor((thumbWidth - tipWidth) / 2);
const monitor = Main.layoutManager.findMonitorForActor(this);
const x = Math.clamp(
stageX + xOffset,
monitor.x,
monitor.x + monitor.width - tipWidth);
const y = stageY - tipHeight - TOOLTIP_OFFSET;
this.set_position(x, y);
this.ease({
opacity: 255,
duration: TOOLTIP_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => (this.visible = this._widget.hover),
});
}
close() {
this.ease({
opacity: 0,
duration: TOOLTIP_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => (this.visible = this._widget.hover),
});
}
} }

View File

@@ -3,11 +3,10 @@ extension_data += configure_file(
output: metadata_name, output: metadata_name,
configuration: metadata_conf configuration: metadata_conf
) )
extension_data += files('stylesheet.css') extension_data += files(
'stylesheet-dark.css',
'stylesheet-light.css'
)
extension_sources += files('prefs.js', 'windowPicker.js', 'workspaceIndicator.js') extension_sources += files('prefs.js', 'windowPicker.js', 'workspaceIndicator.js')
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml') extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
if classic_mode_enabled
extension_data += files('classic.css')
endif

View File

@@ -1,29 +1,24 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- // -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
/* exported init buildPrefsWidget */ import Adw from 'gi://Adw';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Gtk from 'gi://Gtk';
const {Adw, Gio, GLib, GObject, Gtk} = imports.gi; import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
const ExtensionUtils = imports.misc.extensionUtils;
const _ = ExtensionUtils.gettext;
/** */
function init() {
ExtensionUtils.initTranslations();
}
class WindowListPrefsWidget extends Adw.PreferencesPage { class WindowListPrefsWidget extends Adw.PreferencesPage {
static { static {
GObject.registerClass(this); GObject.registerClass(this);
} }
constructor() { constructor(settings) {
super(); super();
this._actionGroup = new Gio.SimpleActionGroup(); this._actionGroup = new Gio.SimpleActionGroup();
this.insert_action_group('window-list', this._actionGroup); this.insert_action_group('window-list', this._actionGroup);
this._settings = ExtensionUtils.getSettings(); this._settings = settings;
this._actionGroup.add_action( this._actionGroup.add_action(
this._settings.create_action('grouping-mode')); this._settings.create_action('grouping-mode'));
this._actionGroup.add_action( this._actionGroup.add_action(
@@ -84,9 +79,8 @@ class WindowListPrefsWidget extends Adw.PreferencesPage {
} }
} }
/** export default class WindowListPrefs extends ExtensionPreferences {
* @returns {Gtk.Widget} - the prefs widget getPreferencesWidget() {
*/ return new WindowListPrefsWidget(this.getSettings());
function buildPrefsWidget() { }
return new WindowListPrefsWidget();
} }

View File

@@ -0,0 +1,113 @@
.window-list {
spacing: 2px;
font-size: 10pt;
}
.bottom-panel {
background-color: #000000;
border-top-width: 0px;
padding: 2px;
}
.window-button {
padding: 2px, 1px;
}
.window-button:first-child:ltr {
padding-left: 2px;
}
.window-button:last-child:rtl {
padding-right: 2px;
}
.window-button-box {
spacing: 4px;
}
.window-button > StWidget,
.window-picker-toggle > StWidget {
color: #bbb;
background-color: #1d1d1d;
border-radius: 4px;
padding: 3px 6px 1px;
transition: 100ms ease;
}
.window-button > StWidget {
-st-natural-width: 18.75em;
max-width: 18.75em;
}
.window-button:hover > StWidget,
.window-picker-toggle:hover > StWidget {
color: #fff;
background-color: #303030;
}
.window-button:active > StWidget,
.window-button:focus > StWidget {
color: #fff;
background-color: #3f3f3f;
}
.window-button.focused > StWidget,
.window-picker-toggle:checked > StWidget {
color: #fff;
background-color: #3f3f3f;
}
.window-button.focused:active > StWidget,
.window-picker-toggle:checked:active > StWidget {
color: #fff;
background-color: #3f3f3f;
}
.window-button.minimized > StWidget {
color: #666;
background-color: #161616;
}
.window-button.minimized:active > StWidget {
color: #666;
background-color: #161616;
}
.window-button-icon {
width: 24px;
height: 24px;
}
.window-list-workspace-indicator .status-label-bin {
background-color: rgba(200, 200, 200, 0.3);
padding: 0 3px;
margin: 3px;
}
.window-list-workspace-indicator .workspaces-box {
spacing: 3px;
padding: 3px;
}
.window-list-workspace-indicator .workspace {
width: 52px;
border-radius: 4px;
background-color: #1e1e1e;
}
.window-list-workspace-indicator .workspace.active {
background-color: #3f3f3f;
}
.window-list-window-preview {
background-color: #bebebe;
border-radius: 1px;
}
.window-list-window-preview.active {
background-color: #d4d4d4;
}
.notification {
font-weight: normal;
}

View File

@@ -1,4 +1,4 @@
@import url("stylesheet.css"); @import url("stylesheet-dark.css");
#panel.bottom-panel { #panel.bottom-panel {
border-top-width: 1px; border-top-width: 1px;

View File

@@ -1,115 +0,0 @@
.window-list {
spacing: 2px;
font-size: 10pt;
}
.window-button {
padding: 1px;
}
.window-button:first-child:ltr {
padding-left: 2px;
}
.window-button:last-child:rtl {
padding-right: 2px;
}
.window-button-box {
spacing: 4px;
}
.window-button > StWidget,
.window-picker-toggle > StWidget {
color: #bbb;
background-color: black;
border-radius: 2px;
padding: 3px 6px 1px;
box-shadow: inset 1px 1px 4px rgba(255,255,255,0.5);
text-shadow: 1px 1px 4px rgba(0,0,0,0.8);
}
.window-picker-toggle {
padding: 3px;
}
.window-picker-toggle > StWidet {
border: 1px solid rgba(255,255,255,0.3);
}
.window-button > StWidget {
-st-natural-width: 18.75em;
max-width: 18.75em;
}
.window-button:hover > StWidget,
.window-picker-toggle:hover > StWidget {
color: white;
background-color: #1f1f1f;
}
.window-button:active > StWidget,
.window-button:focus > StWidget {
box-shadow: inset 2px 2px 4px rgba(255,255,255,0.5);
}
.window-button.focused > StWidget,
.window-picker-toggle:checked > StWidget {
color: white;
box-shadow: inset 1px 1px 4px rgba(255,255,255,0.7);
}
.window-button.focused:active > StWidget,
.window-picker-toggle:checked:active > StWidget {
box-shadow: inset 2px 2px 4px rgba(255,255,255,0.7);
}
.window-button.minimized > StWidget {
color: #666;
box-shadow: inset -1px -1px 4px rgba(255,255,255,0.5);
}
.window-button.minimized:active > StWidget {
box-shadow: inset -2px -2px 4px rgba(255,255,255,0.5);
}
.window-button-icon {
width: 24px;
height: 24px;
}
.window-list-workspace-indicator .status-label-bin {
background-color: rgba(200, 200, 200, .3);
border: 1px solid #cccccc;
padding: 0 3px;
margin: 3px;
}
.window-list-workspace-indicator .workspaces-box {
spacing: 3px;
padding: 3px;
}
.window-list-workspace-indicator .workspace {
border: 2px solid #000;
width: 52px;
border-radius: 4px;
background-color: #595959;
}
.window-list-workspace-indicator .workspace.active {
border-color: #fff;
}
.window-list-window-preview {
background-color: #bebebe;
border: 1px solid #828282;
}
.window-list-window-preview.active {
background-color: #d4d4d4;
}
.notification {
font-weight: normal;
}

View File

@@ -1,17 +1,20 @@
/* exported WindowPicker, WindowPickerToggle */ import Clutter from 'gi://Clutter';
const {Clutter, GObject, Shell, St} = imports.gi; import GObject from 'gi://GObject';
import Shell from 'gi://Shell';
import St from 'gi://St';
const Layout = imports.ui.layout; import {Extension, InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js';
const Main = imports.ui.main; import * as Layout from 'resource:///org/gnome/shell/ui/layout.js';
const {WorkspacesDisplay} = imports.ui.workspacesView; import * as Main from 'resource:///org/gnome/shell/ui/main.js';
const Workspace = imports.ui.workspace; import {WorkspacesDisplay} from 'resource:///org/gnome/shell/ui/workspacesView.js';
import * as Workspace from 'resource:///org/gnome/shell/ui/workspace.js';
const {VIGNETTE_BRIGHTNESS} = imports.ui.lightbox; import {VIGNETTE_BRIGHTNESS} from 'resource:///org/gnome/shell/ui/lightbox.js';
const { import {
SIDE_CONTROLS_ANIMATION_TIME, SIDE_CONTROLS_ANIMATION_TIME,
OverviewAdjustment, OverviewAdjustment,
ControlsState, ControlsState
} = imports.ui.overviewControls; } from 'resource:///org/gnome/shell/ui/overviewControls.js';
class MyWorkspacesDisplay extends WorkspacesDisplay { class MyWorkspacesDisplay extends WorkspacesDisplay {
static { static {
@@ -32,12 +35,13 @@ class MyWorkspacesDisplay extends WorkspacesDisplay {
super(controls, workspaceAdjustment, overviewAdjustment); super(controls, workspaceAdjustment, overviewAdjustment);
this._windowPicker = controls;
this._workspaceAdjustment = workspaceAdjustment; this._workspaceAdjustment = workspaceAdjustment;
this._workspaceAdjustment.actor = this; this._workspaceAdjustment.actor = this;
this._nWorkspacesChangedId = workspaceManager.connectObject('notify::n-workspaces',
workspaceManager.connect('notify::n-workspaces', () => this._updateAdjustment(), this);
this._updateAdjustment.bind(this));
this.add_constraint( this.add_constraint(
new Layout.MonitorConstraint({ new Layout.MonitorConstraint({
@@ -48,7 +52,7 @@ class MyWorkspacesDisplay extends WorkspacesDisplay {
prepareToEnterOverview(...args) { prepareToEnterOverview(...args) {
if (!this._scrollEventId) { if (!this._scrollEventId) {
this._scrollEventId = Main.windowPicker.connect('scroll-event', this._scrollEventId = this._windowPicker.connect('scroll-event',
this._onScrollEvent.bind(this)); this._onScrollEvent.bind(this));
} }
@@ -57,7 +61,7 @@ class MyWorkspacesDisplay extends WorkspacesDisplay {
vfunc_hide(...args) { vfunc_hide(...args) {
if (this._scrollEventId > 0) if (this._scrollEventId > 0)
Main.windowPicker.disconnect(this._scrollEventId); this._windowPicker.disconnect(this._scrollEventId);
this._scrollEventId = 0; this._scrollEventId = 0;
super.vfunc_hide(...args); super.vfunc_hide(...args);
@@ -70,86 +74,9 @@ class MyWorkspacesDisplay extends WorkspacesDisplay {
value: workspaceManager.get_active_workspace_index(), value: workspaceManager.get_active_workspace_index(),
}); });
} }
_onDestroy() {
if (this._nWorkspacesChangedId)
global.workspace_manager.disconnect(this._nWorkspacesChangedId);
this._nWorkspacesChangedId = 0;
super._onDestroy();
}
} }
class MyWorkspace extends Workspace.Workspace { export class WindowPicker extends Clutter.Actor {
static {
GObject.registerClass(this);
}
constructor(...args) {
super(...args);
this._adjChangedId =
this._overviewAdjustment.connect('notify::value', () => {
const {value: progress} = this._overviewAdjustment;
const brightness = 1 - (1 - VIGNETTE_BRIGHTNESS) * progress;
for (const bg of this._background?._backgroundGroup ?? []) {
bg.content.set({
vignette: true,
brightness,
});
}
});
}
_onDestroy() {
super._onDestroy();
if (this._adjChangedId)
this._overviewAdjustment.disconnect(this._adjChangedId);
this._adjChangedId = 0;
}
}
class MyWorkspaceBackground extends Workspace.WorkspaceBackground {
static {
GObject.registerClass(this);
}
_updateBorderRadius() {
}
vfunc_allocate(box) {
this.set_allocation(box);
const themeNode = this.get_theme_node();
const contentBox = themeNode.get_content_box(box);
this._bin.allocate(contentBox);
const [contentWidth, contentHeight] = contentBox.get_size();
const monitor = Main.layoutManager.monitors[this._monitorIndex];
const xRatio = contentWidth / this._workarea.width;
const yRatio = contentHeight / this._workarea.height;
const right = area => area.x + area.width;
const bottom = area => area.y + area.height;
const offsets = {
left: xRatio * (this._workarea.x - monitor.x),
right: xRatio * (right(monitor) - right(this._workarea)),
top: yRatio * (this._workarea.y - monitor.y),
bottom: yRatio * (bottom(monitor) - bottom(this._workarea)),
};
contentBox.set_origin(-offsets.left, -offsets.top);
contentBox.set_size(
offsets.left + contentWidth + offsets.right,
offsets.top + contentHeight + offsets.bottom);
this._backgroundGroup.allocate(contentBox);
}
}
var WindowPicker = class WindowPicker extends Clutter.Actor {
static [GObject.signals] = { static [GObject.signals] = {
'open-state-changed': {param_types: [GObject.TYPE_BOOLEAN]}, 'open-state-changed': {param_types: [GObject.TYPE_BOOLEAN]},
}; };
@@ -164,11 +91,11 @@ var WindowPicker = class WindowPicker extends Clutter.Actor {
this._visible = false; this._visible = false;
this._modal = false; this._modal = false;
this._overlayKeyId = 0;
this._stageKeyPressId = 0; this._stageKeyPressId = 0;
this._adjustment = new OverviewAdjustment(this); this._adjustment = new OverviewAdjustment(this);
this._injectionManager = new InjectionManager();
this.connect('destroy', this._onDestroy.bind(this)); this.connect('destroy', this._onDestroy.bind(this));
global.bind_property('screen-width', global.bind_property('screen-width',
@@ -186,21 +113,78 @@ var WindowPicker = class WindowPicker extends Clutter.Actor {
if (!Main.sessionMode.hasOverview) { if (!Main.sessionMode.hasOverview) {
this._injectBackgroundShade(); this._injectBackgroundShade();
this._overlayKeyId = global.display.connect('overlay-key', () => { global.display.connectObject('overlay-key', () => {
if (!this._visible) if (!this._visible)
this.open(); this.open();
else else
this.close(); this.close();
}); }, this);
} }
} }
_injectBackgroundShade() { _injectBackgroundShade() {
this._origWorkspace = Workspace.Workspace; const backgroundProto = Workspace.WorkspaceBackground.prototype;
this._origWorkspaceBackground = Workspace.WorkspaceBackground; this._injectionManager.overrideMethod(backgroundProto, '_updateBorderRadius',
() => {
return function () {};
});
this._injectionManager.overrideMethod(backgroundProto, 'vfunc_allocate',
() => {
/* eslint-disable no-invalid-this */
return function (box) {
this.set_allocation(box);
Workspace.Workspace = MyWorkspace; const themeNode = this.get_theme_node();
Workspace.WorkspaceBackground = MyWorkspaceBackground; const contentBox = themeNode.get_content_box(box);
this._bin.allocate(contentBox);
const [contentWidth, contentHeight] = contentBox.get_size();
const monitor = Main.layoutManager.monitors[this._monitorIndex];
const xRatio = contentWidth / this._workarea.width;
const yRatio = contentHeight / this._workarea.height;
const right = area => area.x + area.width;
const bottom = area => area.y + area.height;
const offsets = {
left: xRatio * (this._workarea.x - monitor.x),
right: xRatio * (right(monitor) - right(this._workarea)),
top: yRatio * (this._workarea.y - monitor.y),
bottom: yRatio * (bottom(monitor) - bottom(this._workarea)),
};
contentBox.set_origin(-offsets.left, -offsets.top);
contentBox.set_size(
offsets.left + contentWidth + offsets.right,
offsets.top + contentHeight + offsets.bottom);
this._backgroundGroup.allocate(contentBox);
};
/* eslint-enable */
});
this._injectionManager.overrideMethod(backgroundProto, 'vfunc_parent_set',
() => {
/* eslint-disable no-invalid-this */
return function () {
setTimeout(() => {
const parent = this.get_parent();
if (!parent)
return;
parent._overviewAdjustment.connectObject('notify::value', () => {
const {value: progress} = parent._overviewAdjustment;
const brightness = 1 - (1 - VIGNETTE_BRIGHTNESS) * progress;
for (const bg of this._backgroundGroup ?? []) {
bg.content.set({
vignette: true,
brightness,
});
}
}, this);
});
};
/* eslint-enable */
});
} }
get visible() { get visible() {
@@ -306,27 +290,15 @@ var WindowPicker = class WindowPicker extends Clutter.Actor {
} }
_onDestroy() { _onDestroy() {
if (this._origWorkspace) this._injectionManager.clear();
Workspace.Workspace = this._origWorkspace;
if (this._origWorkspaceBackground)
Workspace.WorkspaceBackground = this._origWorkspaceBackground;
if (this._monitorsChangedId)
Main.layoutManager.disconnect(this._monitorsChangedId);
this._monitorsChangedId = 0;
if (this._overlayKeyId)
global.display.disconnect(this._overlayKeyId);
this._overlayKeyId = 0;
if (this._stageKeyPressId) if (this._stageKeyPressId)
global.stage.disconnect(this._stageKeyPressId); global.stage.disconnect(this._stageKeyPressId);
this._stageKeyPressId = 0; this._stageKeyPressId = 0;
} }
}; }
var WindowPickerToggle = class WindowPickerToggle extends St.Button { export class WindowPickerToggle extends St.Button {
static { static {
GObject.registerClass(this); GObject.registerClass(this);
} }
@@ -350,15 +322,16 @@ var WindowPickerToggle = class WindowPickerToggle extends St.Button {
toggle_mode: true, toggle_mode: true,
}); });
const {windowPicker} = Extension.lookupByURL(import.meta.url);
this.connect('notify::checked', () => { this.connect('notify::checked', () => {
if (this.checked) if (this.checked)
Main.windowPicker.open(); windowPicker.open();
else else
Main.windowPicker.close(); windowPicker.close();
}); });
Main.windowPicker.connect('open-state-changed', () => { windowPicker.connect('open-state-changed', () => {
this.checked = Main.windowPicker.visible; this.checked = windowPicker.visible;
}); });
} }
}; }

View File

@@ -1,13 +1,15 @@
/* exported WorkspaceIndicator */ import Clutter from 'gi://Clutter';
const {Clutter, Gio, GObject, Meta, St} = imports.gi; import Gio from 'gi://Gio';
import GObject from 'gi://GObject';
import Meta from 'gi://Meta';
import St from 'gi://St';
const DND = imports.ui.dnd; import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const _ = ExtensionUtils.gettext; import * as DND from 'resource:///org/gnome/shell/ui/dnd.js';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
const TOOLTIP_OFFSET = 6; const TOOLTIP_OFFSET = 6;
const TOOLTIP_ANIMATION_TIME = 150; const TOOLTIP_ANIMATION_TIME = 150;
@@ -29,20 +31,17 @@ class WindowPreview extends St.Button {
this._window = window; this._window = window;
this.connect('destroy', this._onDestroy.bind(this)); this._window.connectObject(
'size-changed', () => this.queue_relayout(),
this._sizeChangedId = this._window.connect('size-changed', 'position-changed', () => {
() => this.queue_relayout());
this._positionChangedId = this._window.connect('position-changed',
() => {
this._updateVisible(); this._updateVisible();
this.queue_relayout(); this.queue_relayout();
}); },
this._minimizedChangedId = this._window.connect('notify::minimized', 'notify::minimized', this._updateVisible.bind(this),
this._updateVisible.bind(this)); this);
this._focusChangedId = global.display.connect('notify::focus-window', global.display.connectObject('notify::focus-window',
this._onFocusChanged.bind(this)); this._onFocusChanged.bind(this), this);
this._onFocusChanged(); this._onFocusChanged();
} }
@@ -51,13 +50,6 @@ class WindowPreview extends St.Button {
return this._window; return this._window;
} }
_onDestroy() {
this._window.disconnect(this._sizeChangedId);
this._window.disconnect(this._positionChangedId);
this._window.disconnect(this._minimizedChangedId);
global.display.disconnect(this._focusChangedId);
}
_onFocusChanged() { _onFocusChanged() {
if (global.display.focus_window === this._window) if (global.display.focus_window === this._window)
this.add_style_class_name('active'); this.add_style_class_name('active');
@@ -138,16 +130,13 @@ class WorkspaceThumbnail extends St.Button {
let workspaceManager = global.workspace_manager; let workspaceManager = global.workspace_manager;
this._workspace = workspaceManager.get_workspace_by_index(index); this._workspace = workspaceManager.get_workspace_by_index(index);
this._windowAddedId = this._workspace.connect('window-added', this._workspace.connectObject(
(ws, window) => { 'window-added', (ws, window) => this._addWindow(window),
this._addWindow(window); 'window-removed', (ws, window) => this._removeWindow(window),
}); this);
this._windowRemovedId = this._workspace.connect('window-removed',
(ws, window) => { global.display.connectObject('restacked',
this._removeWindow(window); this._onRestacked.bind(this), this);
});
this._restackedId = global.display.connect('restacked',
this._onRestacked.bind(this));
this._workspace.list_windows().forEach(w => this._addWindow(w)); this._workspace.list_windows().forEach(w => this._addWindow(w));
this._onRestacked(); this._onRestacked();
@@ -245,20 +234,16 @@ class WorkspaceThumbnail extends St.Button {
_onDestroy() { _onDestroy() {
this._tooltip.destroy(); this._tooltip.destroy();
this._workspace.disconnect(this._windowAddedId);
this._workspace.disconnect(this._windowRemovedId);
global.display.disconnect(this._restackedId);
} }
} }
var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { export class WorkspaceIndicator extends PanelMenu.Button {
static { static {
GObject.registerClass(this); GObject.registerClass(this);
} }
constructor() { constructor() {
super(0.0, _('Workspace Indicator'), true); super(0.5, _('Workspace Indicator'), true);
this.setMenu(new PopupMenu.PopupMenu(this, 0.0, St.Side.BOTTOM)); this.setMenu(new PopupMenu.PopupMenu(this, 0.0, St.Side.BOTTOM));
this.add_style_class_name('window-list-workspace-indicator'); this.add_style_class_name('window-list-workspace-indicator');
this.remove_style_class_name('panel-button'); this.remove_style_class_name('panel-button');
@@ -295,14 +280,11 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button {
this._workspacesItems = []; this._workspacesItems = [];
this._workspaceManagerSignals = [ workspaceManager.connectObject(
workspaceManager.connect('notify::n-workspaces', 'notify::n-workspaces', this._nWorkspacesChanged.bind(this), GObject.ConnectFlags.AFTER,
this._nWorkspacesChanged.bind(this)), 'workspace-switched', this._onWorkspaceSwitched.bind(this), GObject.ConnectFlags.AFTER,
workspaceManager.connect_after('workspace-switched', 'notify::layout-rows', this._updateThumbnailVisibility.bind(this),
this._onWorkspaceSwitched.bind(this)), this);
workspaceManager.connect('notify::layout-rows',
this._updateThumbnailVisibility.bind(this)),
];
this.connect('scroll-event', this._onScrollEvent.bind(this)); this.connect('scroll-event', this._onScrollEvent.bind(this));
this._updateMenu(); this._updateMenu();
@@ -310,20 +292,8 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button {
this._updateThumbnailVisibility(); this._updateThumbnailVisibility();
this._settings = new Gio.Settings({schema_id: 'org.gnome.desktop.wm.preferences'}); this._settings = new Gio.Settings({schema_id: 'org.gnome.desktop.wm.preferences'});
this._settingsChangedId = this._settings.connect( this._settings.connectObject('changed::workspace-names',
'changed::workspace-names', this._updateMenuLabels.bind(this)); () => this._updateMenuLabels(), this);
}
_onDestroy() {
for (let i = 0; i < this._workspaceManagerSignals.length; i++)
global.workspace_manager.disconnect(this._workspaceManagerSignals[i]);
if (this._settingsChangedId) {
this._settings.disconnect(this._settingsChangedId);
this._settingsChangedId = 0;
}
super._onDestroy();
} }
_updateThumbnailVisibility() { _updateThumbnailVisibility() {
@@ -447,4 +417,4 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button {
let newIndex = this._currentWorkspace + diff; let newIndex = this._currentWorkspace + diff;
this._activate(newIndex); this._activate(newIndex);
} }
}; }

View File

@@ -1,276 +1,290 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
/* exported init */ import Clutter from 'gi://Clutter';
const {Clutter, Graphene, GObject, St} = imports.gi; import Graphene from 'gi://Graphene';
import St from 'gi://St';
const Main = imports.ui.main; import * as Main from 'resource:///org/gnome/shell/ui/main.js';
const OverviewControls = imports.ui.overviewControls; import * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js';
const Workspace = imports.ui.workspace; import {InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js';
const WorkspacesView = imports.ui.workspacesView; import {WindowPreview} from 'resource:///org/gnome/shell/ui/windowPreview.js';
import {Workspace} from 'resource:///org/gnome/shell/ui/workspace.js';
import {WorkspacesView} from 'resource:///org/gnome/shell/ui/workspacesView.js';
const WINDOW_SLOT = 4; const WINDOW_SLOT = 4;
class MyWorkspace extends Workspace.Workspace { export default class Extension {
static {
GObject.registerClass(this);
}
constructor(...args) {
super(...args);
if (this.metaWorkspace && this.metaWorkspace.index() < 9) {
this._tip = new St.Label({
style_class: 'extension-windowsNavigator-window-tooltip',
visible: false,
});
this.add_actor(this._tip);
this.connect('notify::scale-x', () => {
this._tip.set_scale(1 / this.scale_x, 1 / this.scale_x);
});
} else {
this._tip = null;
}
}
vfunc_allocate(box) {
super.vfunc_allocate(box);
if (this._tip)
this._tip.allocate_preferred_size(0, 0);
}
showTooltip() {
if (!this._tip)
return;
this._tip.text = (this.metaWorkspace.index() + 1).toString();
this._tip.show();
this.set_child_below_sibling(this._tip, null);
}
hideTooltip() {
if (this._tip)
this._tip.hide();
}
getWindowWithTooltip(id) {
const {layoutManager} = this._container;
const slot = layoutManager._windowSlots[id - 1];
return slot ? slot[WINDOW_SLOT].metaWindow : null;
}
showWindowsTooltips() {
const {layoutManager} = this._container;
for (let i = 0; i < layoutManager._windowSlots.length; i++) {
if (layoutManager._windowSlots[i])
layoutManager._windowSlots[i][WINDOW_SLOT].showTooltip(`${i + 1}`);
}
}
hideWindowsTooltips() {
const {layoutManager} = this._container;
for (let i in layoutManager._windowSlots) {
if (layoutManager._windowSlots[i])
layoutManager._windowSlots[i][WINDOW_SLOT].hideTooltip();
}
}
// overriding _addWindowClone to apply the tooltip patch on the cloned
// windowPreview
_addWindowClone(metaWindow) {
const clone = super._addWindowClone(metaWindow);
// appling the tooltip patch
(function patchPreview() {
this._text = new St.Label({
style_class: 'extension-windowsNavigator-window-tooltip',
visible: false,
});
this._text.add_constraint(new Clutter.BindConstraint({
source: this.windowContainer,
coordinate: Clutter.BindCoordinate.POSITION,
}));
this._text.add_constraint(new Clutter.AlignConstraint({
source: this.windowContainer,
align_axis: Clutter.AlignAxis.X_AXIS,
pivot_point: new Graphene.Point({x: 0.5, y: -1}),
factor: this._closeButtonSide === St.Side.LEFT ? 1 : 0,
}));
this._text.add_constraint(new Clutter.AlignConstraint({
source: this.windowContainer,
align_axis: Clutter.AlignAxis.Y_AXIS,
pivot_point: new Graphene.Point({x: -1, y: 0.5}),
factor: 0,
}));
this.add_child(this._text);
}).call(clone);
clone.showTooltip = function (text) {
this._text.set({text});
this._text.show();
};
clone.hideTooltip = function () {
if (this._text && this._text.visible)
this._text.hide();
};
return clone;
}
}
class MyWorkspacesView extends WorkspacesView.WorkspacesView {
static {
GObject.registerClass(this);
}
constructor(...args) {
super(...args);
this._pickWorkspace = false;
this._pickWindow = false;
this._keyPressEventId =
global.stage.connect('key-press-event', this._onKeyPress.bind(this));
this._keyReleaseEventId =
global.stage.connect('key-release-event', this._onKeyRelease.bind(this));
}
_onDestroy() {
super._onDestroy();
global.stage.disconnect(this._keyPressEventId);
global.stage.disconnect(this._keyReleaseEventId);
}
_hideTooltips() {
if (global.stage.get_key_focus() === global.stage)
global.stage.set_key_focus(this._prevFocusActor);
this._pickWindow = false;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].hideWindowsTooltips();
}
_hideWorkspacesTooltips() {
global.stage.set_key_focus(this._prevFocusActor);
this._pickWorkspace = false;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].hideTooltip();
}
_onKeyRelease(s, o) {
if (this._pickWindow &&
(o.get_key_symbol() === Clutter.KEY_Alt_L ||
o.get_key_symbol() === Clutter.KEY_Alt_R))
this._hideTooltips();
if (this._pickWorkspace &&
(o.get_key_symbol() === Clutter.KEY_Control_L ||
o.get_key_symbol() === Clutter.KEY_Control_R))
this._hideWorkspacesTooltips();
}
_onKeyPress(s, o) {
const {ControlsState} = OverviewControls;
if (this._overviewAdjustment.value !== ControlsState.WINDOW_PICKER)
return false;
let workspaceManager = global.workspace_manager;
if ((o.get_key_symbol() === Clutter.KEY_Alt_L ||
o.get_key_symbol() === Clutter.KEY_Alt_R) &&
!this._pickWorkspace) {
this._prevFocusActor = global.stage.get_key_focus();
global.stage.set_key_focus(null);
this._active = workspaceManager.get_active_workspace_index();
this._pickWindow = true;
this._workspaces[workspaceManager.get_active_workspace_index()].showWindowsTooltips();
return true;
}
if ((o.get_key_symbol() === Clutter.KEY_Control_L ||
o.get_key_symbol() === Clutter.KEY_Control_R) &&
!this._pickWindow) {
this._prevFocusActor = global.stage.get_key_focus();
global.stage.set_key_focus(null);
this._pickWorkspace = true;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].showTooltip();
return true;
}
if (global.stage.get_key_focus() !== global.stage)
return false;
// ignore shift presses, they're required to get numerals in azerty keyboards
if ((this._pickWindow || this._pickWorkspace) &&
(o.get_key_symbol() === Clutter.KEY_Shift_L ||
o.get_key_symbol() === Clutter.KEY_Shift_R))
return true;
if (this._pickWindow) {
if (this._active !== workspaceManager.get_active_workspace_index()) {
this._hideTooltips();
return false;
}
let c = o.get_key_symbol() - Clutter.KEY_KP_0;
if (c > 9 || c <= 0) {
c = o.get_key_symbol() - Clutter.KEY_0;
if (c > 9 || c <= 0) {
this._hideTooltips();
global.log(c);
return false;
}
}
let win = this._workspaces[this._active].getWindowWithTooltip(c);
this._hideTooltips();
if (win)
Main.activateWindow(win, global.get_current_time());
return true;
}
if (this._pickWorkspace) {
let c = o.get_key_symbol() - Clutter.KEY_KP_0;
if (c > 9 || c <= 0) {
c = o.get_key_symbol() - Clutter.KEY_0;
if (c > 9 || c <= 0) {
this._hideWorkspacesTooltips();
return false;
}
}
let workspace = this._workspaces[c - 1];
if (workspace !== undefined)
workspace.metaWorkspace.activate(global.get_current_time());
this._hideWorkspacesTooltips();
return true;
}
return false;
}
}
class Extension {
constructor() { constructor() {
this._origWorkspace = Workspace.Workspace; this._injectionManager = new InjectionManager();
this._origWorkspacesView = WorkspacesView.WorkspacesView;
} }
enable() { enable() {
Workspace.Workspace = MyWorkspace; const previewProto = WindowPreview.prototype;
WorkspacesView.WorkspacesView = MyWorkspacesView;
this._injectionManager.overrideMethod(previewProto, '_init', originalMethod => {
/* eslint-disable no-invalid-this */
return function (...args) {
originalMethod.call(this, ...args);
this._text = new St.Label({
style_class: 'extension-windowsNavigator-window-tooltip',
visible: false,
});
this._text.add_constraint(new Clutter.BindConstraint({
source: this.windowContainer,
coordinate: Clutter.BindCoordinate.POSITION,
}));
this._text.add_constraint(new Clutter.AlignConstraint({
source: this.windowContainer,
align_axis: Clutter.AlignAxis.X_AXIS,
pivot_point: new Graphene.Point({x: 0.5, y: -1}),
factor: this._closeButtonSide === St.Side.LEFT ? 1 : 0,
}));
this._text.add_constraint(new Clutter.AlignConstraint({
source: this.windowContainer,
align_axis: Clutter.AlignAxis.Y_AXIS,
pivot_point: new Graphene.Point({x: -1, y: 0.5}),
factor: 0,
}));
this.add_child(this._text);
};
/* eslint-enable */
});
this._injectionManager.overrideMethod(previewProto, 'showTooltip', () => {
/* eslint-disable no-invalid-this */
return function (text) {
this._text.set({text});
this._text.show();
};
/* eslint-enable */
});
this._injectionManager.overrideMethod(previewProto, 'hideTooltip', () => {
/* eslint-disable no-invalid-this */
return function () {
this._text?.hide();
};
/* eslint-enable */
});
const workspaceProto = Workspace.prototype;
this._injectionManager.overrideMethod(workspaceProto, '_init', originalMethod => {
/* eslint-disable no-invalid-this */
return function (...args) {
originalMethod.call(this, ...args);
if (this.metaWorkspace && this.metaWorkspace.index() < 9) {
this._tip = new St.Label({
style_class: 'extension-windowsNavigator-window-tooltip',
visible: false,
});
this.add_actor(this._tip);
this.connect('notify::scale-x', () => {
this._tip.set_scale(1 / this.scale_x, 1 / this.scale_x);
});
} else {
this._tip = null;
}
};
/* eslint-enable */
});
this._injectionManager.overrideMethod(workspaceProto, 'vfunc_allocate', originalMethod => {
/* eslint-disable no-invalid-this */
return function (box) {
originalMethod.call(this, box);
this._tip?.allocate_preferred_size(0, 0);
};
/* eslint-enable */
});
this._injectionManager.overrideMethod(workspaceProto, 'showTooltip', () => {
/* eslint-disable no-invalid-this */
return function () {
if (!this._tip)
return;
this._tip.text = (this.metaWorkspace.index() + 1).toString();
this._tip.show();
this.set_child_below_sibling(this._tip, null);
};
/* eslint-enable */
});
this._injectionManager.overrideMethod(workspaceProto, 'hideTooltip', () => {
/* eslint-disable no-invalid-this */
return function () {
this._tip?.hide();
};
/* eslint-enable */
});
this._injectionManager.overrideMethod(workspaceProto, 'getWindowWithTooltip', () => {
/* eslint-disable no-invalid-this */
return function (id) {
const {layoutManager} = this._container;
const slot = layoutManager._windowSlots[id - 1];
return slot ? slot[WINDOW_SLOT].metaWindow : null;
};
/* eslint-enable */
});
this._injectionManager.overrideMethod(workspaceProto, 'showWindowsTooltips', () => {
/* eslint-disable no-invalid-this */
return function () {
const {layoutManager} = this._container;
for (let i = 0; i < layoutManager._windowSlots.length; i++) {
if (layoutManager._windowSlots[i])
layoutManager._windowSlots[i][WINDOW_SLOT].showTooltip(`${i + 1}`);
}
};
/* eslint-enable */
});
this._injectionManager.overrideMethod(workspaceProto, 'hideWindowsTooltips', () => {
/* eslint-disable no-invalid-this */
return function () {
const {layoutManager} = this._container;
for (let i in layoutManager._windowSlots) {
if (layoutManager._windowSlots[i])
layoutManager._windowSlots[i][WINDOW_SLOT].hideTooltip();
}
};
/* eslint-enable */
});
const viewProto = WorkspacesView.prototype;
this._injectionManager.overrideMethod(viewProto, '_init', originalMethod => {
/* eslint-disable no-invalid-this */
return function (...args) {
originalMethod.call(this, ...args);
this._pickWorkspace = false;
this._pickWindow = false;
global.stage.connectObject(
'key-press-event', this._onKeyPress.bind(this),
'key-release-event', this._onKeyRelease.bind(this),
this);
};
/* eslint-enable */
});
this._injectionManager.overrideMethod(viewProto, '_hideTooltips', () => {
/* eslint-disable no-invalid-this */
return function () {
if (global.stage.get_key_focus() === global.stage)
global.stage.set_key_focus(this._prevFocusActor);
this._pickWindow = false;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].hideWindowsTooltips();
};
/* eslint-enable */
});
this._injectionManager.overrideMethod(viewProto, '_hideWorkspacesTooltips', () => {
/* eslint-disable no-invalid-this */
return function () {
global.stage.set_key_focus(this._prevFocusActor);
this._pickWorkspace = false;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].hideTooltip();
};
/* eslint-enable */
});
this._injectionManager.overrideMethod(viewProto, '_onKeyRelease', () => {
/* eslint-disable no-invalid-this */
return function (actor, event) {
if (this._pickWindow &&
(event.get_key_symbol() === Clutter.KEY_Alt_L ||
event.get_key_symbol() === Clutter.KEY_Alt_R))
this._hideTooltips();
if (this._pickWorkspace &&
(event.get_key_symbol() === Clutter.KEY_Control_L ||
event.get_key_symbol() === Clutter.KEY_Control_R))
this._hideWorkspacesTooltips();
};
/* eslint-enable */
});
this._injectionManager.overrideMethod(viewProto, '_onKeyPress', () => {
/* eslint-disable no-invalid-this */
return function (actor, event) {
const {ControlsState} = OverviewControls;
if (this._overviewAdjustment.value !== ControlsState.WINDOW_PICKER)
return false;
let workspaceManager = global.workspace_manager;
if ((event.get_key_symbol() === Clutter.KEY_Alt_L ||
event.get_key_symbol() === Clutter.KEY_Alt_R) &&
!this._pickWorkspace) {
this._prevFocusActor = global.stage.get_key_focus();
global.stage.set_key_focus(null);
this._active = workspaceManager.get_active_workspace_index();
this._pickWindow = true;
this._workspaces[workspaceManager.get_active_workspace_index()].showWindowsTooltips();
return true;
}
if ((event.get_key_symbol() === Clutter.KEY_Control_L ||
event.get_key_symbol() === Clutter.KEY_Control_R) &&
!this._pickWindow) {
this._prevFocusActor = global.stage.get_key_focus();
global.stage.set_key_focus(null);
this._pickWorkspace = true;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].showTooltip();
return true;
}
if (global.stage.get_key_focus() !== global.stage)
return false;
// ignore shift presses, they're required to get numerals in azerty keyboards
if ((this._pickWindow || this._pickWorkspace) &&
(event.get_key_symbol() === Clutter.KEY_Shift_L ||
event.get_key_symbol() === Clutter.KEY_Shift_R))
return true;
if (this._pickWindow) {
if (this._active !== workspaceManager.get_active_workspace_index()) {
this._hideTooltips();
return false;
}
let c = event.get_key_symbol() - Clutter.KEY_KP_0;
if (c > 9 || c <= 0) {
c = event.get_key_symbol() - Clutter.KEY_0;
if (c > 9 || c <= 0) {
this._hideTooltips();
log(c);
return false;
}
}
let win = this._workspaces[this._active].getWindowWithTooltip(c);
this._hideTooltips();
if (win)
Main.activateWindow(win, global.get_current_time());
return true;
}
if (this._pickWorkspace) {
let c = event.get_key_symbol() - Clutter.KEY_KP_0;
if (c > 9 || c <= 0) {
c = event.get_key_symbol() - Clutter.KEY_0;
if (c > 9 || c <= 0) {
this._hideWorkspacesTooltips();
return false;
}
}
let workspace = this._workspaces[c - 1];
if (workspace !== undefined)
workspace.metaWorkspace.activate(global.get_current_time());
this._hideWorkspacesTooltips();
return true;
}
return false;
};
/* eslint-enable */
});
} }
disable() { disable() {
Workspace.Workspace = this._origWorkspace; this._injectionManager.clear();
WorkspacesView.WorkspacesView = this._origWorkspacesView;
} }
} }
/**
* @returns {Extension} - the extension's state object
*/
function init() {
return new Extension();
}

View File

@@ -1,15 +1,16 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- // -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
/* exported init enable disable */ import Clutter from 'gi://Clutter';
import Gio from 'gi://Gio';
import GObject from 'gi://GObject';
import Meta from 'gi://Meta';
import St from 'gi://St';
const {Clutter, Gio, GObject, Meta, St} = imports.gi; import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
const DND = imports.ui.dnd; import * as DND from 'resource:///org/gnome/shell/ui/dnd.js';
const ExtensionUtils = imports.misc.extensionUtils; import * as Main from 'resource:///org/gnome/shell/ui/main.js';
const Main = imports.ui.main; import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
const PanelMenu = imports.ui.panelMenu; import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
const PopupMenu = imports.ui.popupMenu;
const _ = ExtensionUtils.gettext;
const WORKSPACE_SCHEMA = 'org.gnome.desktop.wm.preferences'; const WORKSPACE_SCHEMA = 'org.gnome.desktop.wm.preferences';
const WORKSPACE_KEY = 'workspace-names'; const WORKSPACE_KEY = 'workspace-names';
@@ -34,20 +35,17 @@ class WindowPreview extends St.Button {
this._window = window; this._window = window;
this.connect('destroy', this._onDestroy.bind(this)); this._window.connectObject(
'size-changed', () => this.queue_relayout(),
this._sizeChangedId = this._window.connect('size-changed', 'position-changed', () => {
() => this.queue_relayout());
this._positionChangedId = this._window.connect('position-changed',
() => {
this._updateVisible(); this._updateVisible();
this.queue_relayout(); this.queue_relayout();
}); },
this._minimizedChangedId = this._window.connect('notify::minimized', 'notify::minimized', this._updateVisible.bind(this),
this._updateVisible.bind(this)); this);
this._focusChangedId = global.display.connect('notify::focus-window', global.display.connectObject('notify::focus-window',
this._onFocusChanged.bind(this)); this._onFocusChanged.bind(this), this);
this._onFocusChanged(); this._onFocusChanged();
} }
@@ -56,13 +54,6 @@ class WindowPreview extends St.Button {
return this._window; return this._window;
} }
_onDestroy() {
this._window.disconnect(this._sizeChangedId);
this._window.disconnect(this._positionChangedId);
this._window.disconnect(this._minimizedChangedId);
global.display.disconnect(this._focusChangedId);
}
_onFocusChanged() { _onFocusChanged() {
if (global.display.focus_window === this._window) if (global.display.focus_window === this._window)
this.add_style_class_name('active'); this.add_style_class_name('active');
@@ -143,16 +134,13 @@ class WorkspaceThumbnail extends St.Button {
let workspaceManager = global.workspace_manager; let workspaceManager = global.workspace_manager;
this._workspace = workspaceManager.get_workspace_by_index(index); this._workspace = workspaceManager.get_workspace_by_index(index);
this._windowAddedId = this._workspace.connect('window-added', this._workspace.connectObject(
(ws, window) => { 'window-added', (ws, window) => this._addWindow(window),
this._addWindow(window); 'window-removed', (ws, window) => this._removeWindow(window),
}); this);
this._windowRemovedId = this._workspace.connect('window-removed',
(ws, window) => { global.display.connectObject('restacked',
this._removeWindow(window); this._onRestacked.bind(this), this);
});
this._restackedId = global.display.connect('restacked',
this._onRestacked.bind(this));
this._workspace.list_windows().forEach(w => this._addWindow(w)); this._workspace.list_windows().forEach(w => this._addWindow(w));
this._onRestacked(); this._onRestacked();
@@ -250,10 +238,6 @@ class WorkspaceThumbnail extends St.Button {
_onDestroy() { _onDestroy() {
this._tooltip.destroy(); this._tooltip.destroy();
this._workspace.disconnect(this._windowAddedId);
this._workspace.disconnect(this._windowRemovedId);
global.display.disconnect(this._restackedId);
} }
} }
@@ -263,7 +247,7 @@ class WorkspaceIndicator extends PanelMenu.Button {
} }
constructor() { constructor() {
super(0.0, _('Workspace Indicator')); super(0.5, _('Workspace Indicator'));
let container = new St.Widget({ let container = new St.Widget({
layout_manager: new Clutter.BinLayout(), layout_manager: new Clutter.BinLayout(),
@@ -295,14 +279,11 @@ class WorkspaceIndicator extends PanelMenu.Button {
this._workspaceSection = new PopupMenu.PopupMenuSection(); this._workspaceSection = new PopupMenu.PopupMenuSection();
this.menu.addMenuItem(this._workspaceSection); this.menu.addMenuItem(this._workspaceSection);
this._workspaceManagerSignals = [ workspaceManager.connectObject(
workspaceManager.connect_after('notify::n-workspaces', 'notify::n-workspaces', this._nWorkspacesChanged.bind(this), GObject.ConnectFlags.AFTER,
this._nWorkspacesChanged.bind(this)), 'workspace-switched', this._onWorkspaceSwitched.bind(this), GObject.ConnectFlags.AFTER,
workspaceManager.connect_after('workspace-switched', 'notify::layout-rows', this._updateThumbnailVisibility.bind(this),
this._onWorkspaceSwitched.bind(this)), this);
workspaceManager.connect('notify::layout-rows',
this._updateThumbnailVisibility.bind(this)),
];
this.connect('scroll-event', this._onScrollEvent.bind(this)); this.connect('scroll-event', this._onScrollEvent.bind(this));
this._thumbnailsBox.connect('scroll-event', this._onScrollEvent.bind(this)); this._thumbnailsBox.connect('scroll-event', this._onScrollEvent.bind(this));
@@ -311,20 +292,11 @@ class WorkspaceIndicator extends PanelMenu.Button {
this._updateThumbnailVisibility(); this._updateThumbnailVisibility();
this._settings = new Gio.Settings({schema_id: WORKSPACE_SCHEMA}); this._settings = new Gio.Settings({schema_id: WORKSPACE_SCHEMA});
this._settingsChangedId = this._settings.connect( this._settings.connectObject(`changed::${WORKSPACE_KEY}`,
`changed::${WORKSPACE_KEY}`, this._updateMenuLabels.bind(this), this);
this._updateMenuLabels.bind(this));
} }
_onDestroy() { _onDestroy() {
for (let i = 0; i < this._workspaceManagerSignals.length; i++)
global.workspace_manager.disconnect(this._workspaceManagerSignals[i]);
if (this._settingsChangedId) {
this._settings.disconnect(this._settingsChangedId);
this._settingsChangedId = 0;
}
Main.panel.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); Main.panel.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
super._onDestroy(); super._onDestroy();
@@ -454,20 +426,14 @@ class WorkspaceIndicator extends PanelMenu.Button {
} }
} }
/** */ export default class WorkspaceIndicatorExtension extends Extension {
function init() { enable() {
ExtensionUtils.initTranslations(); this._indicator = new WorkspaceIndicator();
} Main.panel.addToStatusArea('workspace-indicator', this._indicator);
}
let _indicator; disable() {
this._indicator.destroy();
/** */ delete this._indicator;
function enable() { }
_indicator = new WorkspaceIndicator();
Main.panel.addToStatusArea('workspace-indicator', _indicator);
}
/** */
function disable() {
_indicator.destroy();
} }

View File

@@ -1,11 +1,13 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- // -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
/* exported init buildPrefsWidget */ import Adw from 'gi://Adw';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Gtk from 'gi://Gtk';
import Pango from 'gi://Pango';
const {Adw, Gio, GLib, GObject, Gtk, Pango} = imports.gi; import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
const ExtensionUtils = imports.misc.extensionUtils;
const _ = ExtensionUtils.gettext;
const N_ = e => e; const N_ = e => e;
const WORKSPACE_SCHEMA = 'org.gnome.desktop.wm.preferences'; const WORKSPACE_SCHEMA = 'org.gnome.desktop.wm.preferences';
@@ -256,14 +258,8 @@ class NewWorkspaceRow extends Adw.PreferencesRow {
} }
} }
/** */ export default class WorkspaceIndicatorPrefs extends ExtensionPreferences {
function init() { getPreferencesWidget() {
ExtensionUtils.initTranslations(); return new WorkspaceSettingsWidget();
} }
/**
* @returns {Gtk.Widget} - the prefs widget
*/
function buildPrefsWidget() {
return new WorkspaceSettingsWidget();
} }

View File

@@ -68,7 +68,10 @@ rules:
jsdoc/check-tag-names: error jsdoc/check-tag-names: error
jsdoc/check-types: error jsdoc/check-types: error
jsdoc/implements-on-classes: error jsdoc/implements-on-classes: error
jsdoc/newline-after-description: error jsdoc/tag-lines:
- error
- any
- startLines: 1
jsdoc/require-jsdoc: error jsdoc/require-jsdoc: error
jsdoc/require-param: error jsdoc/require-param: error
jsdoc/require-param-description: error jsdoc/require-param-description: error

View File

@@ -10,3 +10,5 @@ rules:
prefer-arrow-callback: error prefer-arrow-callback: error
globals: globals:
global: readonly global: readonly
parserOptions:
sourceType: module

View File

@@ -1,6 +1,6 @@
project('gnome-shell-extensions', project('gnome-shell-extensions',
version: '43.1', version: '45.beta',
meson_version: '>= 0.53.0', meson_version: '>= 0.58.0',
license: 'GPL2+' license: 'GPL2+'
) )
@@ -15,7 +15,6 @@ datadir = get_option('datadir')
shelldir = join_paths(datadir, 'gnome-shell') shelldir = join_paths(datadir, 'gnome-shell')
extensiondir = join_paths(shelldir, 'extensions') extensiondir = join_paths(shelldir, 'extensions')
modedir = join_paths(shelldir, 'modes') modedir = join_paths(shelldir, 'modes')
themedir = join_paths(shelldir, 'theme')
schemadir = join_paths(datadir, 'glib-2.0', 'schemas') schemadir = join_paths(datadir, 'glib-2.0', 'schemas')
sessiondir = join_paths(datadir, 'gnome-session', 'sessions') sessiondir = join_paths(datadir, 'gnome-session', 'sessions')
@@ -37,6 +36,7 @@ classic_extensions = [
default_extensions = classic_extensions default_extensions = classic_extensions
default_extensions += [ default_extensions += [
'drive-menu', 'drive-menu',
'light-style',
'screenshot-window-sizer', 'screenshot-window-sizer',
'windowsNavigator', 'windowsNavigator',
'workspace-indicator' 'workspace-indicator'
@@ -93,7 +93,23 @@ endif
subdir('extensions') subdir('extensions')
subdir('po') subdir('po')
meson.add_dist_script('meson/generate-stylesheets.py') gnome.post_install(
glib_compile_schemas: true,
)
meson.add_dist_script('meson/check-version.py', meson.add_dist_script('meson/check-version.py',
meson.project_version(), meson.project_version(),
'NEWS') 'NEWS')
summary_options = {
'extensions': enabled_extensions,
'classic_mode': get_option('classic_mode'),
}
summary_dirs = {
'prefix': get_option('prefix'),
'datadir': get_option('datadir'),
}
summary(summary_dirs, section: 'Directories')
summary(summary_options, section: 'Build Options')

View File

@@ -1,13 +0,0 @@
#!/usr/bin/env python3
import os
from pathlib import PurePath
import subprocess
sourceroot = os.environ.get('MESON_SOURCE_ROOT')
distroot = os.environ.get('MESON_DIST_ROOT')
stylesheet_path = PurePath('data/gnome-classic.css')
src = PurePath(sourceroot, stylesheet_path.with_suffix('.scss'))
dst = PurePath(distroot, stylesheet_path)
subprocess.run(['sassc', '-a', src, dst], check=True)

101
po/be.po
View File

@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: gnome-shell-extensions master\n" "Project-Id-Version: gnome-shell-extensions master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell-extensions/" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell-extensions/"
"issues\n" "issues\n"
"POT-Creation-Date: 2021-01-20 23:03+0000\n" "POT-Creation-Date: 2022-07-10 12:54+0000\n"
"PO-Revision-Date: 2021-01-24 15:33+0300\n" "PO-Revision-Date: 2022-10-19 15:20+0300\n"
"Last-Translator: Launchpad translators\n" "Last-Translator: Launchpad translators\n"
"Language-Team: Belarusian <i18n-bel-gnome@googlegroups.com>\n" "Language-Team: Belarusian <i18n-bel-gnome@googlegroups.com>\n"
"Language: be\n" "Language: be\n"
@@ -18,21 +18,30 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Poedit 2.4.2\n" "X-Generator: Poedit 3.0\n"
#: data/gnome-classic.desktop.in:3 #: data/gnome-classic.desktop.in:3
msgid "GNOME Classic" msgid "GNOME Classic"
msgstr "Класічны GNOME" msgstr "Класічны GNOME"
#: data/gnome-classic.desktop.in:4 #: data/gnome-classic.desktop.in:4 data/gnome-classic-wayland.desktop.in:4
#: data/gnome-classic-xorg.desktop.in:4
msgid "This session logs you into GNOME Classic" msgid "This session logs you into GNOME Classic"
msgstr "Гэты сеанс выкарыстоўвае класічны GNOME" msgstr "Гэты сеанс выкарыстоўвае класічны GNOME"
#: extensions/apps-menu/extension.js:113 #: data/gnome-classic-wayland.desktop.in:3
msgid "GNOME Classic on Wayland"
msgstr "Класічны GNOME на Wayland"
#: data/gnome-classic-xorg.desktop.in:3
msgid "GNOME Classic on Xorg"
msgstr "Класічны GNOME на Xorg"
#: extensions/apps-menu/extension.js:118
msgid "Favorites" msgid "Favorites"
msgstr "Абраныя" msgstr "Абраныя"
#: extensions/apps-menu/extension.js:369 #: extensions/apps-menu/extension.js:379
msgid "Applications" msgid "Applications"
msgstr "Праграмы" msgstr "Праграмы"
@@ -48,43 +57,41 @@ msgstr ""
"Спіс радкоў, кожны з якіх змяшчае ідэнтыфікатар праграмы (імя файла *." "Спіс радкоў, кожны з якіх змяшчае ідэнтыфікатар праграмы (імя файла *."
"desktop), затым двукроп'е і нумар працоўнай прасторы" "desktop), затым двукроп'е і нумар працоўнай прасторы"
#: extensions/auto-move-windows/prefs.js:35 #: extensions/auto-move-windows/prefs.js:152
msgid "Workspace Rules" msgid "Workspace Rules"
msgstr "Правілы для працоўнай прасторы" msgstr "Правілы для працоўнай прасторы"
#: extensions/auto-move-windows/prefs.js:237 #: extensions/auto-move-windows/prefs.js:306
msgid "Add Rule" msgid "Add Rule"
msgstr "Дадаць правіла" msgstr "Дадаць правіла"
#. TRANSLATORS: %s is the filesystem name #. TRANSLATORS: %s is the filesystem name
#: extensions/drive-menu/extension.js:112 #: extensions/drive-menu/extension.js:126
#: extensions/places-menu/placeDisplay.js:233 #: extensions/places-menu/placeDisplay.js:210
#, javascript-format #, javascript-format
msgid "Ejecting drive “%s” failed:" msgid "Ejecting drive “%s” failed:"
msgstr "Не ўдалося выняць дыск «%s»:" msgstr "Не ўдалося выняць дыск «%s»:"
#: extensions/drive-menu/extension.js:128 #: extensions/drive-menu/extension.js:145
msgid "Removable devices" msgid "Removable devices"
msgstr "Здымныя прылады" msgstr "Здымныя прылады"
#: extensions/drive-menu/extension.js:155 #: extensions/drive-menu/extension.js:167
msgid "Open Files" msgid "Open Files"
msgstr "Адкрыць файлы" msgstr "Адкрыць файлы"
#: extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml:5 #: extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml:5
#, fuzzy
msgid "Use more screen for windows" msgid "Use more screen for windows"
msgstr "Выкарыстоўваць большую плошчу экрана для вокнаў" msgstr "Выкарыстоўваць большую плошчу экрана для вокнаў"
#: extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml:6 #: extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml:6
#, fuzzy
msgid "" msgid ""
"Try to use more screen for placing window thumbnails by adapting to screen " "Try to use more screen for placing window thumbnails by adapting to screen "
"aspect ratio, and consolidating them further to reduce the bounding box. " "aspect ratio, and consolidating them further to reduce the bounding box. "
"This setting applies only with the natural placement strategy." "This setting applies only with the natural placement strategy."
msgstr "" msgstr ""
"Спрабаваць выкарыстаць большую плошчу экрана для размяшчэння мініяцюр праз " "Спрабаваць выкарыстаць большую плошчу экрана для размяшчэння мініяцюр праз "
"змяненне суадносінаў бакоў экрана, ўшчыльняючы іх, каб зменшыць памеры " "змяненне суадносін бакоў экрана, ўшчыльняючы іх, каб зменшыць памеры "
"абмежавальнай рамкі. Гэты параметр ужываецца толькі з натуральным " "абмежавальнай рамкі. Гэты параметр ужываецца толькі з натуральным "
"размяшчэннем мініяцюр." "размяшчэннем мініяцюр."
@@ -102,31 +109,31 @@ msgstr ""
"перадвызначана). Каб змена налады ўступіла ў сілу, трэба перазапусціць " "перадвызначана). Каб змена налады ўступіла ў сілу, трэба перазапусціць "
"абалонку." "абалонку."
#: extensions/places-menu/extension.js:89 #: extensions/places-menu/extension.js:94
#: extensions/places-menu/extension.js:93 #: extensions/places-menu/extension.js:97
msgid "Places" msgid "Places"
msgstr "Месцы" msgstr "Месцы"
#: extensions/places-menu/placeDisplay.js:46 #: extensions/places-menu/placeDisplay.js:49
#, javascript-format #, javascript-format
msgid "Failed to launch “%s”" msgid "Failed to launch “%s”"
msgstr "Не ўдалося запусціць «%s»" msgstr "Не ўдалося запусціць «%s»"
#: extensions/places-menu/placeDisplay.js:61 #: extensions/places-menu/placeDisplay.js:64
#, javascript-format #, javascript-format
msgid "Failed to mount volume for “%s”" msgid "Failed to mount volume for “%s”"
msgstr "Не ўдалося прымацаваць том для «%s»." msgstr "Не ўдалося прымацаваць том для «%s»."
#: extensions/places-menu/placeDisplay.js:125
#: extensions/places-menu/placeDisplay.js:148 #: extensions/places-menu/placeDisplay.js:148
#: extensions/places-menu/placeDisplay.js:171
msgid "Computer" msgid "Computer"
msgstr "Камп'ютар" msgstr "Камп'ютар"
#: extensions/places-menu/placeDisplay.js:359 #: extensions/places-menu/placeDisplay.js:336
msgid "Home" msgid "Home"
msgstr "Хатняя папка" msgstr "Хатняя папка"
#: extensions/places-menu/placeDisplay.js:404 #: extensions/places-menu/placeDisplay.js:381
msgid "Browse Network" msgid "Browse Network"
msgstr "Агляд сеткі" msgstr "Агляд сеткі"
@@ -146,47 +153,47 @@ msgstr "Назва тэмы"
msgid "The name of the theme, to be loaded from ~/.themes/name/gnome-shell" msgid "The name of the theme, to be loaded from ~/.themes/name/gnome-shell"
msgstr "Назва тэмы, што загрузіцца з ~/.themes/name/gnome-shell" msgstr "Назва тэмы, што загрузіцца з ~/.themes/name/gnome-shell"
#: extensions/window-list/extension.js:98 #: extensions/window-list/extension.js:72
msgid "Close" msgid "Close"
msgstr "Закрыць" msgstr "Закрыць"
#: extensions/window-list/extension.js:118 #: extensions/window-list/extension.js:92
msgid "Unminimize" msgid "Unminimize"
msgstr "Скасаваць згортванне" msgstr "Скасаваць згортванне"
#: extensions/window-list/extension.js:118 #: extensions/window-list/extension.js:92
msgid "Minimize" msgid "Minimize"
msgstr "Згарнуць" msgstr "Згарнуць"
#: extensions/window-list/extension.js:125 #: extensions/window-list/extension.js:99
msgid "Unmaximize" msgid "Unmaximize"
msgstr "Скасаваць разгортванне" msgstr "Скасаваць разгортванне"
#: extensions/window-list/extension.js:125 #: extensions/window-list/extension.js:99
msgid "Maximize" msgid "Maximize"
msgstr "Разгарнуць" msgstr "Разгарнуць"
#: extensions/window-list/extension.js:432 #: extensions/window-list/extension.js:483
msgid "Minimize all" msgid "Minimize all"
msgstr "Згарнуць усе" msgstr "Згарнуць усе"
#: extensions/window-list/extension.js:438 #: extensions/window-list/extension.js:489
msgid "Unminimize all" msgid "Unminimize all"
msgstr "Скасаваць згортванне для ўсіх" msgstr "Скасаваць згортванне для ўсіх"
#: extensions/window-list/extension.js:444 #: extensions/window-list/extension.js:495
msgid "Maximize all" msgid "Maximize all"
msgstr "Разгарнуць усе" msgstr "Разгарнуць усе"
#: extensions/window-list/extension.js:452 #: extensions/window-list/extension.js:503
msgid "Unmaximize all" msgid "Unmaximize all"
msgstr "Скасаваць разгортванне для ўсіх" msgstr "Скасаваць разгортванне для ўсіх"
#: extensions/window-list/extension.js:460 #: extensions/window-list/extension.js:511
msgid "Close all" msgid "Close all"
msgstr "Закрыць усе" msgstr "Закрыць усе"
#: extensions/window-list/extension.js:737 #: extensions/window-list/extension.js:795
msgid "Window List" msgid "Window List"
msgstr "Спіс вокнаў" msgstr "Спіс вокнаў"
@@ -203,7 +210,7 @@ msgstr ""
"значэнні: «never» (ніколі), «auto» (аўтаматычна), «always» (заўсёды)." "значэнні: «never» (ніколі), «auto» (аўтаматычна), «always» (заўсёды)."
#: extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml:20 #: extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml:20
#: extensions/window-list/prefs.js:100 #: extensions/window-list/prefs.js:79
msgid "Show windows from all workspaces" msgid "Show windows from all workspaces"
msgstr "Паказваць вокны з усіх працоўных прастор" msgstr "Паказваць вокны з усіх працоўных прастор"
@@ -222,41 +229,41 @@ msgid ""
msgstr "" msgstr ""
"Паказваць спіс вокнаў на ўсіх падлучаных маніторах ці толькі на асноўным." "Паказваць спіс вокнаў на ўсіх падлучаных маніторах ці толькі на асноўным."
#: extensions/window-list/prefs.js:29 #: extensions/window-list/prefs.js:35
msgid "Window Grouping" msgid "Window Grouping"
msgstr "Групаванне вокнаў" msgstr "Групаванне вокнаў"
#: extensions/window-list/prefs.js:58 #: extensions/window-list/prefs.js:40
msgid "Never group windows" msgid "Never group windows"
msgstr "Ніколі не групаваць вокны" msgstr "Ніколі не групаваць вокны"
#: extensions/window-list/prefs.js:59 #: extensions/window-list/prefs.js:41
msgid "Group windows when space is limited" msgid "Group windows when space is limited"
msgstr "Групаваць вокны калі не хапае месца" msgstr "Групаваць вокны калі не хапае месца"
#: extensions/window-list/prefs.js:60 #: extensions/window-list/prefs.js:42
msgid "Always group windows" msgid "Always group windows"
msgstr "Заўсёды групаваць вокны" msgstr "Заўсёды групаваць вокны"
#: extensions/window-list/prefs.js:94 #: extensions/window-list/prefs.js:66
msgid "Show on all monitors" msgid "Show on all monitors"
msgstr "Паказваць на ўсіх маніторах" msgstr "Паказваць на ўсіх маніторах"
#: extensions/window-list/workspaceIndicator.js:247 #: extensions/window-list/workspaceIndicator.js:261
#: extensions/workspace-indicator/extension.js:253 #: extensions/workspace-indicator/extension.js:266
msgid "Workspace Indicator" msgid "Workspace Indicator"
msgstr "Індыкатар працоўнай прасторы" msgstr "Індыкатар працоўнай прасторы"
#: extensions/workspace-indicator/prefs.js:34 #: extensions/workspace-indicator/prefs.js:62
msgid "Workspace Names"
msgstr "Назвы працоўных прастор"
#: extensions/workspace-indicator/prefs.js:67
#, javascript-format #, javascript-format
msgid "Workspace %d" msgid "Workspace %d"
msgstr "Працоўная прастора %d" msgstr "Працоўная прастора %d"
#: extensions/workspace-indicator/prefs.js:208 #: extensions/workspace-indicator/prefs.js:129
msgid "Workspace Names"
msgstr "Назвы працоўных прастор"
#: extensions/workspace-indicator/prefs.js:255
msgid "Add Workspace" msgid "Add Workspace"
msgstr "Дадаць працоўную прастору" msgstr "Дадаць працоўную прастору"

101
po/el.po
View File

@@ -12,8 +12,8 @@ msgstr ""
"Project-Id-Version: gnome-shell-extensions master\n" "Project-Id-Version: gnome-shell-extensions master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell-extensions/" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell-extensions/"
"issues\n" "issues\n"
"POT-Creation-Date: 2020-05-28 00:55+0000\n" "POT-Creation-Date: 2023-02-18 15:10+0000\n"
"PO-Revision-Date: 2020-07-14 00:40+0300\n" "PO-Revision-Date: 2023-08-01 23:41+0300\n"
"Last-Translator: Efstathios Iosifidis <eiosifidis@gnome.org>\n" "Last-Translator: Efstathios Iosifidis <eiosifidis@gnome.org>\n"
"Language-Team: Greek, Modern (1453-) <gnome-el-list@gnome.org>\n" "Language-Team: Greek, Modern (1453-) <gnome-el-list@gnome.org>\n"
"Language: el\n" "Language: el\n"
@@ -21,22 +21,31 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.8.7.1\n" "X-Generator: Poedit 3.3.2\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: data/gnome-classic.desktop.in:3 data/gnome-classic.session.desktop.in:3 #: data/gnome-classic.desktop.in:3
msgid "GNOME Classic" msgid "GNOME Classic"
msgstr "GNOME Classic" msgstr "GNOME Classic"
#: data/gnome-classic.desktop.in:4 #: data/gnome-classic.desktop.in:4 data/gnome-classic-wayland.desktop.in:4
#: data/gnome-classic-xorg.desktop.in:4
msgid "This session logs you into GNOME Classic" msgid "This session logs you into GNOME Classic"
msgstr "Αυτή η συνεδρία σας συνδέει στο GNOME Classic" msgstr "Αυτή η συνεδρία σας συνδέει στο GNOME Classic"
#: extensions/apps-menu/extension.js:113 #: data/gnome-classic-wayland.desktop.in:3
msgid "GNOME Classic on Wayland"
msgstr "GNOME Classic σε Wayland"
#: data/gnome-classic-xorg.desktop.in:3
msgid "GNOME Classic on Xorg"
msgstr "GNOME Classic σε Xorg"
#: extensions/apps-menu/extension.js:118
msgid "Favorites" msgid "Favorites"
msgstr "Αγαπημένα" msgstr "Αγαπημένα"
#: extensions/apps-menu/extension.js:369 #: extensions/apps-menu/extension.js:380
msgid "Applications" msgid "Applications"
msgstr "Εφαρμογές" msgstr "Εφαρμογές"
@@ -53,26 +62,26 @@ msgstr ""
"(όνομα αρχείου επιφάνειας εργασίας), ακολουθούμενη από άνω-κάτω τελεία και " "(όνομα αρχείου επιφάνειας εργασίας), ακολουθούμενη από άνω-κάτω τελεία και "
"τον αριθμό του χώρου εργασίας" "τον αριθμό του χώρου εργασίας"
#: extensions/auto-move-windows/prefs.js:35 #: extensions/auto-move-windows/prefs.js:152
msgid "Workspace Rules" msgid "Workspace Rules"
msgstr "Κανόνες χώρων εργασίας" msgstr "Κανόνες χώρων εργασίας"
#: extensions/auto-move-windows/prefs.js:243 #: extensions/auto-move-windows/prefs.js:306
msgid "Add Rule" msgid "Add Rule"
msgstr "Προσθήκη κανόνα" msgstr "Προσθήκη κανόνα"
#. TRANSLATORS: %s is the filesystem name #. TRANSLATORS: %s is the filesystem name
#: extensions/drive-menu/extension.js:112 #: extensions/drive-menu/extension.js:126
#: extensions/places-menu/placeDisplay.js:233 #: extensions/places-menu/placeDisplay.js:212
#, javascript-format #, javascript-format
msgid "Ejecting drive “%s” failed:" msgid "Ejecting drive “%s” failed:"
msgstr "Αποτυχία εξαγωγής του δίσκου «%s»:" msgstr "Αποτυχία εξαγωγής του δίσκου «%s»:"
#: extensions/drive-menu/extension.js:128 #: extensions/drive-menu/extension.js:145
msgid "Removable devices" msgid "Removable devices"
msgstr "Αφαιρούμενες συσκευές" msgstr "Αφαιρούμενες συσκευές"
#: extensions/drive-menu/extension.js:155 #: extensions/drive-menu/extension.js:167
msgid "Open Files" msgid "Open Files"
msgstr "Άνοιγμα αρχείων" msgstr "Άνοιγμα αρχείων"
@@ -106,31 +115,31 @@ msgstr ""
"στο κάτω μέρος. Η αλλαγή αυτής της ρύθμισης απαιτεί επανεκκίνηση του " "στο κάτω μέρος. Η αλλαγή αυτής της ρύθμισης απαιτεί επανεκκίνηση του "
"κελύφους για να υπάρξει κάποιο αποτέλεσμα." "κελύφους για να υπάρξει κάποιο αποτέλεσμα."
#: extensions/places-menu/extension.js:89 #: extensions/places-menu/extension.js:94
#: extensions/places-menu/extension.js:93 #: extensions/places-menu/extension.js:97
msgid "Places" msgid "Places"
msgstr "Τοποθεσίες" msgstr "Τοποθεσίες"
#: extensions/places-menu/placeDisplay.js:46 #: extensions/places-menu/placeDisplay.js:52
#, javascript-format #, javascript-format
msgid "Failed to launch “%s”" msgid "Failed to launch “%s”"
msgstr "Αποτυχία εκκίνησης «%s»" msgstr "Αποτυχία εκκίνησης «%s»"
#: extensions/places-menu/placeDisplay.js:61 #: extensions/places-menu/placeDisplay.js:67
#, javascript-format #, javascript-format
msgid "Failed to mount volume for “%s”" msgid "Failed to mount volume for “%s”"
msgstr "Αποτυχία προσάρτησης τόμου για «%s»" msgstr "Αποτυχία προσάρτησης τόμου για «%s»"
#: extensions/places-menu/placeDisplay.js:148 #: extensions/places-menu/placeDisplay.js:127
#: extensions/places-menu/placeDisplay.js:171 #: extensions/places-menu/placeDisplay.js:150
msgid "Computer" msgid "Computer"
msgstr "Υπολογιστής" msgstr "Υπολογιστής"
#: extensions/places-menu/placeDisplay.js:359 #: extensions/places-menu/placeDisplay.js:340
msgid "Home" msgid "Home"
msgstr "Προσωπικός φάκελος" msgstr "Προσωπικός φάκελος"
#: extensions/places-menu/placeDisplay.js:404 #: extensions/places-menu/placeDisplay.js:385
msgid "Browse Network" msgid "Browse Network"
msgstr "Περιήγηση δικτύου" msgstr "Περιήγηση δικτύου"
@@ -151,47 +160,47 @@ msgid "The name of the theme, to be loaded from ~/.themes/name/gnome-shell"
msgstr "" msgstr ""
"Το όνομα του θέματος που θα φορτωθεί από το ~ /.themes/name/gnome-shell" "Το όνομα του θέματος που θα φορτωθεί από το ~ /.themes/name/gnome-shell"
#: extensions/window-list/extension.js:98 #: extensions/window-list/extension.js:72
msgid "Close" msgid "Close"
msgstr "Κλείσιμο" msgstr "Κλείσιμο"
#: extensions/window-list/extension.js:118 #: extensions/window-list/extension.js:92
msgid "Unminimize" msgid "Unminimize"
msgstr "Αποελαχιστοποίηση" msgstr "Αποελαχιστοποίηση"
#: extensions/window-list/extension.js:118 #: extensions/window-list/extension.js:92
msgid "Minimize" msgid "Minimize"
msgstr "Ελαχιστοποίηση" msgstr "Ελαχιστοποίηση"
#: extensions/window-list/extension.js:125 #: extensions/window-list/extension.js:99
msgid "Unmaximize" msgid "Unmaximize"
msgstr "Απομεγιστοποίηση" msgstr "Απομεγιστοποίηση"
#: extensions/window-list/extension.js:125 #: extensions/window-list/extension.js:99
msgid "Maximize" msgid "Maximize"
msgstr "Μεγιστοποίηση" msgstr "Μεγιστοποίηση"
#: extensions/window-list/extension.js:428 #: extensions/window-list/extension.js:483
msgid "Minimize all" msgid "Minimize all"
msgstr "Ελαχιστοποίηση όλων" msgstr "Ελαχιστοποίηση όλων"
#: extensions/window-list/extension.js:434 #: extensions/window-list/extension.js:489
msgid "Unminimize all" msgid "Unminimize all"
msgstr "Αποελαχιστοποίηση όλων" msgstr "Αποελαχιστοποίηση όλων"
#: extensions/window-list/extension.js:440 #: extensions/window-list/extension.js:495
msgid "Maximize all" msgid "Maximize all"
msgstr "Μεγιστοποίηση όλων" msgstr "Μεγιστοποίηση όλων"
#: extensions/window-list/extension.js:448 #: extensions/window-list/extension.js:503
msgid "Unmaximize all" msgid "Unmaximize all"
msgstr "Απομεγιστοποίηση όλων" msgstr "Απομεγιστοποίηση όλων"
#: extensions/window-list/extension.js:456 #: extensions/window-list/extension.js:511
msgid "Close all" msgid "Close all"
msgstr "Κλείσιμο όλων" msgstr "Κλείσιμο όλων"
#: extensions/window-list/extension.js:734 #: extensions/window-list/extension.js:795
msgid "Window List" msgid "Window List"
msgstr "Λίστα παραθύρου" msgstr "Λίστα παραθύρου"
@@ -209,7 +218,7 @@ msgstr ""
"«always» (πάντα)." "«always» (πάντα)."
#: extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml:20 #: extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml:20
#: extensions/window-list/prefs.js:100 #: extensions/window-list/prefs.js:79
msgid "Show windows from all workspaces" msgid "Show windows from all workspaces"
msgstr "Εμφάνιση των παραθύρων από όλους τους χώρους εργασίας" msgstr "Εμφάνιση των παραθύρων από όλους τους χώρους εργασίας"
@@ -230,41 +239,41 @@ msgstr ""
"Αν θα εμφανίζεται ο κατάλογος παραθύρων όλων των συνδεμένων οθονών ή μόνο " "Αν θα εμφανίζεται ο κατάλογος παραθύρων όλων των συνδεμένων οθονών ή μόνο "
"της κύριας οθόνης." "της κύριας οθόνης."
#: extensions/window-list/prefs.js:29 #: extensions/window-list/prefs.js:35
msgid "Window Grouping" msgid "Window Grouping"
msgstr "Ομαδοποίηση παραθύρου" msgstr "Ομαδοποίηση παραθύρου"
#: extensions/window-list/prefs.js:58 #: extensions/window-list/prefs.js:40
msgid "Never group windows" msgid "Never group windows"
msgstr "Να μη γίνεται ποτέ ομαδοποίηση παραθύρων" msgstr "Να μη γίνεται ποτέ ομαδοποίηση παραθύρων"
#: extensions/window-list/prefs.js:59 #: extensions/window-list/prefs.js:41
msgid "Group windows when space is limited" msgid "Group windows when space is limited"
msgstr "Ομαδοποίηση παραθύρων όταν ο χώρος είναι περιορισμένος" msgstr "Ομαδοποίηση παραθύρων όταν ο χώρος είναι περιορισμένος"
#: extensions/window-list/prefs.js:60 #: extensions/window-list/prefs.js:42
msgid "Always group windows" msgid "Always group windows"
msgstr "Να γίνεται πάντα ομαδοποίηση παραθύρων" msgstr "Να γίνεται πάντα ομαδοποίηση παραθύρων"
#: extensions/window-list/prefs.js:94 #: extensions/window-list/prefs.js:66
msgid "Show on all monitors" msgid "Show on all monitors"
msgstr "Να εμφανίζεται σε όλες τις οθόνες" msgstr "Να εμφανίζεται σε όλες τις οθόνες"
#: extensions/window-list/workspaceIndicator.js:207 #: extensions/window-list/workspaceIndicator.js:261
#: extensions/workspace-indicator/extension.js:213 #: extensions/workspace-indicator/extension.js:266
msgid "Workspace Indicator" msgid "Workspace Indicator"
msgstr "Δείκτης χώρου εργασίας" msgstr "Δείκτης χώρου εργασίας"
#: extensions/workspace-indicator/prefs.js:34 #: extensions/workspace-indicator/prefs.js:62
msgid "Workspace Names"
msgstr "Ονόματα χώρων εργασίας:"
#: extensions/workspace-indicator/prefs.js:67
#, javascript-format #, javascript-format
msgid "Workspace %d" msgid "Workspace %d"
msgstr "Χώρος εργασίας %d" msgstr "Χώρος εργασίας %d"
#: extensions/workspace-indicator/prefs.js:218 #: extensions/workspace-indicator/prefs.js:129
msgid "Workspace Names"
msgstr "Ονόματα χώρων εργασίας"
#: extensions/workspace-indicator/prefs.js:255
msgid "Add Workspace" msgid "Add Workspace"
msgstr "Προσθήκη χώρου εργασίας" msgstr "Προσθήκη χώρου εργασίας"