Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ef8d50556 | |||
| 222eaa04c9 | |||
| b4f602e0dd | |||
| d43ee6831a | |||
| aa3ca5ff4a | |||
| beb3e4acfb | |||
| 71b03c8878 | |||
| fe6a391cd0 | |||
| 1fc86978d2 | |||
| 3c100c393a | |||
| 2e1856ab34 | |||
| 0861745de4 |
@@ -1,3 +0,0 @@
|
|||||||
extends:
|
|
||||||
- ./lint/eslintrc-gjs.yml
|
|
||||||
- ./lint/eslintrc-shell.yml
|
|
||||||
+20
-4
@@ -1,7 +1,23 @@
|
|||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
Makefile.in.in
|
||||||
|
configure
|
||||||
|
config.log
|
||||||
|
config.status
|
||||||
|
aclocal.m4
|
||||||
|
autom4te.cache/
|
||||||
|
data/*.json
|
||||||
po/gnome-shell-extensions.pot
|
po/gnome-shell-extensions.pot
|
||||||
|
po/POTFILES
|
||||||
|
po/stamp-it
|
||||||
|
staging/
|
||||||
zip-files/
|
zip-files/
|
||||||
|
|
||||||
*~
|
*~
|
||||||
*.patch
|
*.gmo
|
||||||
*.sw?
|
metadata.json
|
||||||
.buildconfig
|
*.desktop
|
||||||
.vscode
|
*.desktop.in
|
||||||
|
*.gschema.xml
|
||||||
|
*.gschema.valid
|
||||||
|
*.session
|
||||||
|
|||||||
-169
@@ -1,169 +0,0 @@
|
|||||||
include:
|
|
||||||
- remote: 'https://gitlab.freedesktop.org/freedesktop/ci-templates/-/raw/bbe5232986c9b98eb1efe62484e07216f7d1a4df/templates/fedora.yml'
|
|
||||||
- remote: "https://gitlab.freedesktop.org/freedesktop/ci-templates/-/raw/6f86b8bcb0cd5168c32779c4fea9a893c4a0c046/templates/ci-fairy.yml"
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- pre_review
|
|
||||||
- prepare
|
|
||||||
- review
|
|
||||||
- build
|
|
||||||
- deploy
|
|
||||||
|
|
||||||
default:
|
|
||||||
image: registry.gitlab.gnome.org/gnome/gnome-shell/fedora/35:2022-02-18.0
|
|
||||||
# Cancel jobs if newer commits are pushed to the branch
|
|
||||||
interruptible: true
|
|
||||||
# Auto-retry jobs in case of infra failures
|
|
||||||
retry:
|
|
||||||
max: 1
|
|
||||||
when:
|
|
||||||
- 'runner_system_failure'
|
|
||||||
- 'stuck_or_timeout_failure'
|
|
||||||
- 'scheduler_failure'
|
|
||||||
- 'api_failure'
|
|
||||||
|
|
||||||
variables:
|
|
||||||
FDO_UPSTREAM_REPO: GNOME/gnome-shell-extensions
|
|
||||||
LINT_LOG: "eslint-report.xml"
|
|
||||||
|
|
||||||
workflow:
|
|
||||||
rules:
|
|
||||||
- if: '$CI_MERGE_REQUEST_IID'
|
|
||||||
- if: '$CI_COMMIT_TAG'
|
|
||||||
- if: '$CI_COMMIT_BRANCH'
|
|
||||||
|
|
||||||
.pipeline_guard: &pipeline_guard
|
|
||||||
rules:
|
|
||||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
|
||||||
- if: '$CI_COMMIT_TAG'
|
|
||||||
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
||||||
- if: '$CI_COMMIT_BRANCH =~ /^gnome-[0-9-]+$/'
|
|
||||||
- when: 'manual'
|
|
||||||
|
|
||||||
.gnome-shell-extensions.fedora:34:
|
|
||||||
variables:
|
|
||||||
FDO_DISTRIBUTION_VERSION: 34
|
|
||||||
FDO_DISTRIBUTION_TAG: '2021-08-31.0'
|
|
||||||
FDO_DISTRIBUTION_PACKAGES: >
|
|
||||||
meson git gettext sassc
|
|
||||||
|
|
||||||
.prereview_req: &prereview_req
|
|
||||||
needs:
|
|
||||||
- check_commit_log
|
|
||||||
- check-merge-request
|
|
||||||
|
|
||||||
check_commit_log:
|
|
||||||
extends:
|
|
||||||
- .fdo.ci-fairy
|
|
||||||
stage: pre_review
|
|
||||||
script:
|
|
||||||
- if [[ x"$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" != "x" ]] ;
|
|
||||||
then
|
|
||||||
ci-fairy check-commits --junit-xml=commit-message-junit-report.xml ;
|
|
||||||
else
|
|
||||||
echo "Not a merge request" ;
|
|
||||||
fi
|
|
||||||
<<: *pipeline_guard
|
|
||||||
artifacts:
|
|
||||||
expire_in: 1 week
|
|
||||||
paths:
|
|
||||||
- commit-message-junit-report.xml
|
|
||||||
reports:
|
|
||||||
junit: commit-message-junit-report.xml
|
|
||||||
|
|
||||||
check-merge-request:
|
|
||||||
extends:
|
|
||||||
- .fdo.ci-fairy
|
|
||||||
stage: pre_review
|
|
||||||
script:
|
|
||||||
- if [[ x"$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" != "x" ]] ;
|
|
||||||
then
|
|
||||||
ci-fairy check-merge-request --require-allow-collaboration --junit-xml=check-merge-request-report.xml ;
|
|
||||||
else
|
|
||||||
echo "Not a merge request" ;
|
|
||||||
fi
|
|
||||||
<<: *pipeline_guard
|
|
||||||
artifacts:
|
|
||||||
expire_in: 1 week
|
|
||||||
paths:
|
|
||||||
- check-merge-request-report.xml
|
|
||||||
reports:
|
|
||||||
junit: check-merge-request-report.xml
|
|
||||||
|
|
||||||
build-fedora-container:
|
|
||||||
extends:
|
|
||||||
- .fdo.container-build@fedora@x86_64
|
|
||||||
- .gnome-shell-extensions.fedora:34
|
|
||||||
stage: prepare
|
|
||||||
<<: *prereview_req
|
|
||||||
|
|
||||||
js_check:
|
|
||||||
stage: review
|
|
||||||
<<: *prereview_req
|
|
||||||
script:
|
|
||||||
- gjs-check-syntax
|
|
||||||
|
|
||||||
eslint:
|
|
||||||
stage: review
|
|
||||||
<<: *prereview_req
|
|
||||||
script:
|
|
||||||
- export NODE_PATH=$(npm root -g)
|
|
||||||
- ./.gitlab-ci/run-eslint --output-file ${LINT_LOG} --format junit --stdout
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- ${LINT_LOG}
|
|
||||||
reports:
|
|
||||||
junit: ${LINT_LOG}
|
|
||||||
|
|
||||||
build-bundles:
|
|
||||||
stage: build
|
|
||||||
<<: *prereview_req
|
|
||||||
script:
|
|
||||||
- ./export-zips.sh
|
|
||||||
artifacts:
|
|
||||||
name: 'Extension bundles'
|
|
||||||
expose_as: 'Get Extension bundles here'
|
|
||||||
paths:
|
|
||||||
- zip-files/
|
|
||||||
|
|
||||||
fedora-build:
|
|
||||||
extends:
|
|
||||||
- .fdo.distribution-image@fedora
|
|
||||||
- .gnome-shell-extensions.fedora:34
|
|
||||||
stage: build
|
|
||||||
needs:
|
|
||||||
- build-fedora-container
|
|
||||||
script:
|
|
||||||
- meson setup build --werror -Dextension_set=all -Dclassic_mode=true
|
|
||||||
- meson compile -C build
|
|
||||||
- meson test -C build
|
|
||||||
- meson install -C build
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- build
|
|
||||||
|
|
||||||
fedora-dist:
|
|
||||||
extends:
|
|
||||||
- .fdo.distribution-image@fedora
|
|
||||||
- .gnome-shell-extensions.fedora:34
|
|
||||||
stage: deploy
|
|
||||||
needs:
|
|
||||||
- fedora-build
|
|
||||||
variables:
|
|
||||||
GIT_SUBMODULE_STRATEGY: normal
|
|
||||||
script:
|
|
||||||
- meson dist -C build
|
|
||||||
rules:
|
|
||||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
|
||||||
changes:
|
|
||||||
- "**/meson.build"
|
|
||||||
- meson/*
|
|
||||||
|
|
||||||
fedora-dist-tarball:
|
|
||||||
extends: fedora-dist
|
|
||||||
artifacts:
|
|
||||||
expose_as: 'Get tarball here'
|
|
||||||
paths:
|
|
||||||
- build/meson-dist/$CI_PROJECT_NAME-$CI_COMMIT_TAG.tar.xz
|
|
||||||
rules:
|
|
||||||
- if: '$CI_COMMIT_TAG'
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
patterns:
|
|
||||||
deny:
|
|
||||||
- regex: '^$CI_MERGE_REQUEST_PROJECT_URL/(-/)?merge_requests/$CI_MERGE_REQUEST_IID$'
|
|
||||||
message: Commit message must not contain a link to its own merge request
|
|
||||||
- regex: '^extensions/'
|
|
||||||
message: Commit message subject should not be prefixed with 'extensions/', use the extension name instead
|
|
||||||
where: subject
|
|
||||||
- regex: '^[^:]+: [a-z]'
|
|
||||||
message: "Commit message subject should be properly Capitalized. E.g. 'window: Marginalize extradicity'"
|
|
||||||
where: subject
|
|
||||||
- regex: '^\S*\.js:'
|
|
||||||
message: Commit message subject prefix should not include .js
|
|
||||||
where: subject
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
#!/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);
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
--- Creating a New Extension ---
|
||||||
|
|
||||||
|
To create a new extension, add a subdirectory in extensions.
|
||||||
|
Then create a Makefile.am like the one in example, replacing
|
||||||
|
the EXTENSION_ID with the basename of your extension, which
|
||||||
|
must match the UUID in metadata.json.
|
||||||
|
If you need additional files, add them to EXTENSION_EXTRA.
|
||||||
|
|
||||||
|
Then modify extensions/Makefile.am and configure.ac. It should
|
||||||
|
be pretty self-explanatory.
|
||||||
|
|
||||||
|
Don't forget to add any translatable file to po/POTFILES.in, and
|
||||||
|
then you're done.
|
||||||
|
The Gettext domain you should choose is gnome-shell-extensions,
|
||||||
|
not gnome-shell, unless you're sure there is the string you
|
||||||
|
need in gnome-shell.
|
||||||
|
|
||||||
|
--- Coding Style ---
|
||||||
|
|
||||||
|
Generally, we follow GJS coding style (you can find it at
|
||||||
|
http://git.gnome.org/browse/gjs/tree/doc/Style_Guide.txt), which
|
||||||
|
in short is: indent 4 spaces, no tabs, space after comma, no space
|
||||||
|
after function call.
|
||||||
|
|
||||||
|
The Emacs mode line for this
|
||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
Imports should be at the top, in two groups, one for standard
|
||||||
|
imports (like imports.lang or imports.dbus) and introspection,
|
||||||
|
the other for Shell API. Within the same group, put everything
|
||||||
|
in alphabetic order.
|
||||||
-31
@@ -1,31 +0,0 @@
|
|||||||
## Creating a New Extension
|
|
||||||
|
|
||||||
To create a new extension, add a subdirectory in extensions. Then create
|
|
||||||
a meson.build from the provided [template](extensions/meson.build.template).
|
|
||||||
If you need additional sources, add them to extension_sources. Similarily add
|
|
||||||
GSettings schemas to extension_schemas and other files to extension_data.
|
|
||||||
|
|
||||||
Then modify the [toplevel Meson file](meson.build) to add the new
|
|
||||||
extension name in the appropriate set (that is one of classic_extensions,
|
|
||||||
default_extensions or all_extensions).
|
|
||||||
|
|
||||||
Don't forget to add any translatable file to po/POTFILES.in, and
|
|
||||||
then you're done.
|
|
||||||
The Gettext domain you should choose is gnome-shell-extensions,
|
|
||||||
not gnome-shell, unless you're sure there is the string you
|
|
||||||
need in gnome-shell.
|
|
||||||
|
|
||||||
## Coding Style
|
|
||||||
|
|
||||||
Generally, we follow [GJS coding style][coding-style], which in short is:
|
|
||||||
indent 4 spaces, no tabs, space after comma, no space after function call.
|
|
||||||
|
|
||||||
The Emacs mode line for this
|
|
||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
||||||
|
|
||||||
Imports should be at the top, in two groups, one for standard
|
|
||||||
imports (like imports.lang or imports.dbus) and introspection,
|
|
||||||
the other for Shell API. Within the same group, put everything
|
|
||||||
in alphabetic order.
|
|
||||||
|
|
||||||
[coding-style]: https://gitlab.gnome.org/GNOME/gjs/blob/HEAD/doc/Style_Guide.md
|
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
|
||||||
|
|
||||||
|
DIST_SUBDIRS = data extensions po
|
||||||
|
|
||||||
|
SUBDIRS = extensions po
|
||||||
|
|
||||||
|
if CLASSIC_MODE
|
||||||
|
SUBDIRS += data
|
||||||
|
endif
|
||||||
|
|
||||||
|
EXTRA_DIST = lib/convenience.js
|
||||||
|
|
||||||
|
DISTCHECK_CONFIGURE_FLAGS = --enable-extensions=all
|
||||||
|
|
||||||
|
include include.mk
|
||||||
|
|
||||||
|
zip-file: all
|
||||||
|
-rm -fR $(builddir)/_build
|
||||||
|
-rm -fR $(builddir)/zip-files
|
||||||
|
$(MKDIR_P) $(builddir)/_build; \
|
||||||
|
$(MKDIR_P) $(builddir)/zip-files; \
|
||||||
|
$(MAKE) install DESTDIR="$(abs_builddir)/_build"; \
|
||||||
|
for i in $(ENABLED_EXTENSIONS); do \
|
||||||
|
mv "$(builddir)/_build$(topextensiondir)/$${i}$(extensionbase)" "$(builddir)/_build/"; \
|
||||||
|
cp -r "$(builddir)/_build$(datadir)/locale" "$(builddir)/_build/$${i}$(extensionbase)"; \
|
||||||
|
cp "$(srcdir)/COPYING" -t "$(builddir)/_build/$${i}$(extensionbase)"; \
|
||||||
|
cp "$(srcdir)/NEWS" -t "$(builddir)/_build/$${i}$(extensionbase)"; \
|
||||||
|
if [ -f "$(builddir)/_build$(datadir)/glib-2.0/schemas/$(gschemabase).$${i}.gschema.xml" ]; then \
|
||||||
|
$(MKDIR_P) "$(builddir)/_build/$${i}$(extensionbase)/schemas"; \
|
||||||
|
mv "$(builddir)/_build$(datadir)/glib-2.0/schemas/$(gschemabase).$${i}.gschema.xml" "$(builddir)/_build/$${i}$(extensionbase)/schemas"; \
|
||||||
|
glib-compile-schemas "$(builddir)/_build/$${i}$(extensionbase)/schemas"; \
|
||||||
|
fi; \
|
||||||
|
(cd "$(builddir)/_build/$${i}$(extensionbase)"; \
|
||||||
|
zip -qr "$(abs_builddir)/zip-files/$${i}$(extensionbase).shell-extension.zip" .; \
|
||||||
|
); \
|
||||||
|
done
|
||||||
|
-rm -fR $(builddir)/_build
|
||||||
|
|
||||||
|
localprefix = $(HOME)/.local/share/gnome-shell/extensions
|
||||||
|
|
||||||
|
local-install: zip-file
|
||||||
|
for i in $(ENABLED_EXTENSIONS); do \
|
||||||
|
uuid="$${i}$(extensionbase)"; \
|
||||||
|
zip_file="$(abs_builddir)/zip-files/$${uuid}.shell-extension.zip"; \
|
||||||
|
if [ -d "$(localprefix)/$${uuid}" ]; then \
|
||||||
|
rm -fR "$(localprefix)/$${uuid}"; \
|
||||||
|
fi; \
|
||||||
|
$(MKDIR_P) $(localprefix)/$${uuid}; \
|
||||||
|
(cd $(localprefix)/$${uuid}; \
|
||||||
|
unzip -q $${zip_file}; \
|
||||||
|
); \
|
||||||
|
done
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
GNOME Shell Extensions is a collection of extensions providing additional
|
||||||
|
and optional functionality to GNOME Shell.
|
||||||
|
Since GNOME Shell is not API stable, extensions work only against a very
|
||||||
|
specific version of the shell, usually the same as this package (see
|
||||||
|
"configure --version"). Also, since extensions are built from many
|
||||||
|
individual contributors, we cannot guarantee stability or quality for any
|
||||||
|
specific extension.
|
||||||
|
For these reasons, distributions are advised to avoid installing or packaging
|
||||||
|
this module by defaul.
|
||||||
|
|
||||||
|
For more information about GNOME Shell Extensions
|
||||||
|
http://live.gnome.org/GnomeShell/Extensions
|
||||||
|
|
||||||
|
For general information about GNOME Shell
|
||||||
|
http://live.gnome.org/GnomeShell
|
||||||
|
|
||||||
|
Bugs should be reported at http://bugzilla.gnome.org against the 'gnome-shell'
|
||||||
|
product, with the 'extensions' component.
|
||||||
|
|
||||||
|
Extensions
|
||||||
|
==========
|
||||||
|
|
||||||
|
alternate-tab
|
||||||
|
|
||||||
|
Lets you use classic Alt+Tab (window-based instead of app-based) in GNOME Shell.
|
||||||
|
|
||||||
|
alternative-status-menu
|
||||||
|
|
||||||
|
For those who want a power off item visible at all the time, replaces GNOME Shell
|
||||||
|
status menu with one featuring separate Suspend and Power Off. Adds the ability to
|
||||||
|
hibernate as well.
|
||||||
|
|
||||||
|
apps-menu
|
||||||
|
|
||||||
|
Lets you reach an application using gnome 2.x style menu on the panel.
|
||||||
|
|
||||||
|
auto-move-windows
|
||||||
|
|
||||||
|
Lets you manage your workspaces more easily, assigning a specific workspace to
|
||||||
|
each application as soon as it creates a window, in a manner configurable with a
|
||||||
|
GSettings key.
|
||||||
|
|
||||||
|
dock
|
||||||
|
|
||||||
|
Shows a dock-style task switcher on the right side of the screen.
|
||||||
|
|
||||||
|
drive-menu
|
||||||
|
Shows a status menu for rapid unmount and power off of external storage devices
|
||||||
|
(i.e. pendrives)
|
||||||
|
|
||||||
|
example
|
||||||
|
|
||||||
|
A minimal example illustrating how to write extensions.
|
||||||
|
|
||||||
|
gajim
|
||||||
|
|
||||||
|
Integration with Gajim, a Jabber/XMPP instant messaging client.
|
||||||
|
|
||||||
|
native-window-placement
|
||||||
|
|
||||||
|
An alternative algorithm for layouting the thumbnails in the windows overview, that
|
||||||
|
more closely reflects the actual positions and sizes.
|
||||||
|
|
||||||
|
places-menu
|
||||||
|
|
||||||
|
Shows a status Indicator for navigating to Places.
|
||||||
|
|
||||||
|
systemMonitor
|
||||||
|
|
||||||
|
An message tray indicator showing CPU and memory loads.
|
||||||
|
|
||||||
|
user-theme
|
||||||
|
|
||||||
|
Loads a shell theme from ~/.themes/<name>/gnome-shell.
|
||||||
|
|
||||||
|
windowsNavigator
|
||||||
|
|
||||||
|
Allow keyboard selection of windows and workspaces in overlay mode.
|
||||||
|
|
||||||
|
xrandr-indicator
|
||||||
|
|
||||||
|
Replace the GTK+ based indicator from gnome-settings-daemon with
|
||||||
|
a native one. Lets the user rotate the laptop monitor and open
|
||||||
|
display preferences quickly.
|
||||||
|
|
||||||
|
License
|
||||||
|
=======
|
||||||
|
GNOME Shell Extensions are distributed under the terms of the GNU General Public License,
|
||||||
|
version 2 or later. See the COPYING file for details.
|
||||||
|
Individual extensions may be licensed under different terms, see each source
|
||||||
|
file for details.
|
||||||
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
# GNOME Shell Extensions
|
|
||||||
|
|
||||||
GNOME Shell Extensions is a collection of extensions providing additional
|
|
||||||
and optional functionality to GNOME Shell.
|
|
||||||
|
|
||||||
Since GNOME Shell is not API stable, extensions work only against a very
|
|
||||||
specific version of the shell, usually the same as this package (see
|
|
||||||
"configure --version"). The extensions in this package are supported by GNOME
|
|
||||||
and will be updated to reflect future API changes in GNOME Shell.
|
|
||||||
|
|
||||||
The GNOME wiki has more information about [GNOME Shell Extensions][project-page],
|
|
||||||
as well as some general information about [GNOME Shell][shell-page].
|
|
||||||
|
|
||||||
Bugs should be reported to the GNOME [bug tracking system][bug-tracker].
|
|
||||||
|
|
||||||
## Extensions
|
|
||||||
|
|
||||||
* alternate-tab (**OBSOLETE**)
|
|
||||||
|
|
||||||
Lets you use classic Alt+Tab (window-based instead of app-based) in GNOME Shell.
|
|
||||||
This extension is obsolete since GNOME 3.30, see [this blogpost][alternatetab-post]
|
|
||||||
for further details.
|
|
||||||
|
|
||||||
* apps-menu
|
|
||||||
|
|
||||||
Lets you reach an application using gnome 2.x style menu on the panel.
|
|
||||||
|
|
||||||
* auto-move-windows
|
|
||||||
|
|
||||||
Lets you manage your workspaces more easily, assigning a specific workspace to
|
|
||||||
each application as soon as it creates a window, in a manner configurable with a
|
|
||||||
GSettings key.
|
|
||||||
|
|
||||||
* drive-menu
|
|
||||||
|
|
||||||
Shows a status menu for rapid unmount and power off of external storage devices
|
|
||||||
(i.e. pendrives)
|
|
||||||
|
|
||||||
* launch-new-instance
|
|
||||||
|
|
||||||
Changes application icons to always launch a new instance when activated.
|
|
||||||
|
|
||||||
* native-window-placement
|
|
||||||
|
|
||||||
An alternative algorithm for layouting the thumbnails in the windows overview, that
|
|
||||||
more closely reflects the actual positions and sizes.
|
|
||||||
|
|
||||||
* places-menu
|
|
||||||
|
|
||||||
Shows a status Indicator for navigating to Places.
|
|
||||||
|
|
||||||
* screenshot-window-sizer
|
|
||||||
|
|
||||||
Adds a shortcut for resizing the focus window to a size that is suitable for GNOME Software screenshots
|
|
||||||
|
|
||||||
* user-theme
|
|
||||||
|
|
||||||
Loads a shell theme from ~/.themes/<name>/gnome-shell.
|
|
||||||
|
|
||||||
* window-list
|
|
||||||
|
|
||||||
Adds a bottom panel with a traditional window list.
|
|
||||||
|
|
||||||
* windowsNavigator
|
|
||||||
|
|
||||||
Allow keyboard selection of windows and workspaces in overlay mode.
|
|
||||||
|
|
||||||
* workspace-indicator
|
|
||||||
|
|
||||||
Adds a simple workspace switcher to the top bar.
|
|
||||||
|
|
||||||
## Default branch
|
|
||||||
|
|
||||||
The default development branch is `main`. If you still have a local
|
|
||||||
checkout under the old name, use:
|
|
||||||
```sh
|
|
||||||
git checkout master
|
|
||||||
git branch -m master main
|
|
||||||
git fetch
|
|
||||||
git branch --unset-upstream
|
|
||||||
git branch -u origin/master
|
|
||||||
git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
GNOME Shell Extensions are distributed under the terms of the GNU General
|
|
||||||
Public License, version 2 or later. See the [COPYING file][license] for details.
|
|
||||||
Individual extensions may be licensed under different terms, see each source
|
|
||||||
file for details.
|
|
||||||
|
|
||||||
[project-page]: https://wiki.gnome.org/Projects/GnomeShell/Extensions
|
|
||||||
[shell-page]: https://wiki.gnome.org/Projects/GnomeShell
|
|
||||||
[bug-tracker]: https://gitlab.gnome.org/GNOME/gnome-shell-extensions/issues
|
|
||||||
[license]: COPYING
|
|
||||||
[alternatetab-post]: https://blogs.gnome.org/fmuellner/2018/10/11/the-future-of-alternatetab-and-why-you-need-not-worry/
|
|
||||||
Executable
+20
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Run this to generate all the initial makefiles, etc.
|
||||||
|
|
||||||
|
srcdir=`dirname $0`
|
||||||
|
test -z "$srcdir" && srcdir=.
|
||||||
|
|
||||||
|
PKG_NAME="gnome-shell-extensions"
|
||||||
|
|
||||||
|
test -f $srcdir/configure.ac || {
|
||||||
|
echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
|
||||||
|
echo " top-level gnome-shell-extensions directory"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
which gnome-autogen.sh || {
|
||||||
|
echo "You need to install gnome-common from GNOME Git (or from"
|
||||||
|
echo "your OS vendor's package manager)."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
. gnome-autogen.sh
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
install-sh
|
||||||
|
mkinstalldirs
|
||||||
|
missing
|
||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
AC_PREREQ(2.63)
|
||||||
|
AC_INIT([gnome-shell-extensions],[3.8.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&component=extensions])
|
||||||
|
|
||||||
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
|
AC_CONFIG_AUX_DIR([config])
|
||||||
|
|
||||||
|
AM_INIT_AUTOMAKE([1.10 dist-xz no-dist-gzip foreign tar-ustar])
|
||||||
|
|
||||||
|
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
|
||||||
|
|
||||||
|
GETTEXT_PACKAGE=gnome-shell-extensions
|
||||||
|
AC_SUBST(GETTEXT_PACKAGE)
|
||||||
|
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
|
||||||
|
[The prefix for our gettext translation domains.])
|
||||||
|
IT_PROG_INTLTOOL(0.26)
|
||||||
|
|
||||||
|
PKG_PROG_PKG_CONFIG([0.22])
|
||||||
|
|
||||||
|
GLIB_GSETTINGS
|
||||||
|
|
||||||
|
SHELL_VERSION="$PACKAGE_VERSION"
|
||||||
|
shell_major=`echo "$PACKAGE_VERSION" | cut -d'.' -f1`
|
||||||
|
shell_minor=`echo "$PACKAGE_VERSION" | cut -d'.' -f2`
|
||||||
|
if test "$(($shell_minor % 2))" -eq 0; then
|
||||||
|
SHELL_VERSION="$shell_major.$shell_minor"
|
||||||
|
fi
|
||||||
|
AC_SUBST([SHELL_VERSION])
|
||||||
|
|
||||||
|
dnl keep this in alphabetic order
|
||||||
|
CLASSIC_EXTENSIONS="apps-menu places-menu alternate-tab launch-new-instance window-list"
|
||||||
|
DEFAULT_EXTENSIONS="$CLASSIC_EXTENSIONS alternative-status-menu drive-menu windowsNavigator workspace-indicator"
|
||||||
|
ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows example native-window-placement systemMonitor user-theme xrandr-indicator"
|
||||||
|
AC_SUBST(CLASSIC_EXTENSIONS, [$CLASSIC_EXTENSIONS])
|
||||||
|
AC_SUBST(ALL_EXTENSIONS, [$ALL_EXTENSIONS])
|
||||||
|
AC_ARG_ENABLE([extensions],
|
||||||
|
[AS_HELP_STRING([--enable-extensions],[Space separated list of extensions to enable.
|
||||||
|
The default is to build and distribute all extensions that can be installed in the home directory and have no external depedencies.
|
||||||
|
Use "all" to enable all available extensions, or "classic-mode" to enable extensions that restore GNOME 2 functionality.])],
|
||||||
|
[],
|
||||||
|
[enable_extensions=$DEFAULT_EXTENSIONS]
|
||||||
|
)
|
||||||
|
|
||||||
|
if test x"$enable_extensions" = xall; then
|
||||||
|
enable_extensions="$ALL_EXTENSIONS"
|
||||||
|
enable_classic_mode_default=yes
|
||||||
|
elif test x"$enable_extensions" = xclassic-mode; then
|
||||||
|
enable_extensions="$CLASSIC_EXTENSIONS"
|
||||||
|
enable_classic_mode_default=yes
|
||||||
|
else
|
||||||
|
enable_classic_mode_default=no
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([classic-mode],
|
||||||
|
[AS_HELP_STRING([--enable-classic-mode],[Enable installing data files for classic mode.
|
||||||
|
Defaults to yes if enabling all or classic-mode extensions, and no otherwise.])],
|
||||||
|
[],
|
||||||
|
[enable_classic_mode=$enable_classic_mode_default])
|
||||||
|
AM_CONDITIONAL([CLASSIC_MODE], [test x"$enable_classic_mode" != xno])
|
||||||
|
|
||||||
|
ENABLED_EXTENSIONS=
|
||||||
|
for e in $enable_extensions; do
|
||||||
|
case $e in
|
||||||
|
systemMonitor)
|
||||||
|
PKG_CHECK_MODULES(GTOP, libgtop-2.0 >= 2.28.3,
|
||||||
|
[ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e"],
|
||||||
|
[AC_MSG_WARN([libgtop-2.0 not found, disabling systemMonitor])])
|
||||||
|
;;
|
||||||
|
xrandr-indicator)
|
||||||
|
PKG_CHECK_MODULES(GNOME_DESKTOP, gnome-desktop-3.0 >= 2.91.6,
|
||||||
|
[ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e"],
|
||||||
|
[AC_MSG_WARN([gnome-desktop-3.0 not found, disabling xrandr-indicator])])
|
||||||
|
;;
|
||||||
|
dnl keep this in alphabetic order
|
||||||
|
alternate-tab|alternative-status-menu|apps-menu|auto-move-windows|drive-menu|example|launch-new-instance|native-window-placement|places-menu|user-theme|window-list|windowsNavigator|workspace-indicator)
|
||||||
|
ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
AC_MSG_ERROR([invalid extension $e])
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
AC_SUBST(ENABLED_EXTENSIONS, [$ENABLED_EXTENSIONS])
|
||||||
|
|
||||||
|
dnl Please keep this sorted alphabetically
|
||||||
|
AC_CONFIG_FILES([
|
||||||
|
data/Makefile
|
||||||
|
extensions/alternate-tab/Makefile
|
||||||
|
extensions/alternative-status-menu/Makefile
|
||||||
|
extensions/apps-menu/Makefile
|
||||||
|
extensions/auto-move-windows/Makefile
|
||||||
|
extensions/drive-menu/Makefile
|
||||||
|
extensions/example/Makefile
|
||||||
|
extensions/launch-new-instance/Makefile
|
||||||
|
extensions/native-window-placement/Makefile
|
||||||
|
extensions/places-menu/Makefile
|
||||||
|
extensions/systemMonitor/Makefile
|
||||||
|
extensions/user-theme/Makefile
|
||||||
|
extensions/window-list/Makefile
|
||||||
|
extensions/windowsNavigator/Makefile
|
||||||
|
extensions/workspace-indicator/Makefile
|
||||||
|
extensions/xrandr-indicator/Makefile
|
||||||
|
extensions/Makefile
|
||||||
|
Makefile
|
||||||
|
po/Makefile.in
|
||||||
|
])
|
||||||
|
AC_OUTPUT
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
[org.gnome.mutter:GNOME-Classic]
|
|
||||||
dynamic-workspaces=false
|
|
||||||
|
|
||||||
[org.gnome.desktop.wm.preferences:GNOME-Classic]
|
|
||||||
button-layout='appmenu:minimize,maximize,close'
|
|
||||||
|
|
||||||
[org.gnome.desktop.wm.keybindings:GNOME-Classic]
|
|
||||||
switch-applications=[]
|
|
||||||
switch-windows=['<Super>Tab','<Alt>Tab']
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
To update the gnome-shell-sass submodule to latest upstream commit:
|
|
||||||
git submodule update --rebase
|
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
include $(top_srcdir)/include.mk
|
||||||
|
|
||||||
|
desktopdir = $(datadir)/applications
|
||||||
|
desktop_in_in_files = gnome-shell-classic.desktop.in.in
|
||||||
|
desktop_in_files = $(desktop_in_in_files:.desktop.in.in=.desktop.in)
|
||||||
|
desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
|
||||||
|
|
||||||
|
sessiondir = $(datadir)/gnome-session/sessions
|
||||||
|
session_in_in_files = gnome-classic.session.desktop.in.in
|
||||||
|
session_in_files = $(session_in_in_files:.session.desktop.in.in=.session.desktop.in)
|
||||||
|
session_DATA = $(session_in_files:.session.desktop.in=.session)
|
||||||
|
|
||||||
|
xsessiondir = $(datadir)/xsessions
|
||||||
|
xsession_in_files = gnome-classic.desktop.in
|
||||||
|
xsession_DATA = $(xsession_in_files:.desktop.in=.desktop)
|
||||||
|
|
||||||
|
modedir = $(datadir)/gnome-shell/modes
|
||||||
|
mode_in_files = classic.json.in
|
||||||
|
mode_DATA = $(mode_in_files:.json.in=.json)
|
||||||
|
|
||||||
|
themedir = $(datadir)/gnome-shell/theme
|
||||||
|
theme_DATA = \
|
||||||
|
$(srcdir)/classic-process-working.svg \
|
||||||
|
$(srcdir)/classic-toggle-off-intl.svg \
|
||||||
|
$(srcdir)/classic-toggle-off-us.svg \
|
||||||
|
$(srcdir)/classic-toggle-on-intl.svg \
|
||||||
|
$(srcdir)/classic-toggle-on-us.svg \
|
||||||
|
$(srcdir)/gnome-classic.css \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
gsettings_in_files = org.gnome.shell.extensions.classic-overrides.gschema.xml.in
|
||||||
|
gsettings_SCHEMAS = $(gsettings_in_files:.xml.in=.xml)
|
||||||
|
|
||||||
|
@INTLTOOL_XML_NOMERGE_RULE@
|
||||||
|
@GSETTINGS_RULES@
|
||||||
|
|
||||||
|
%.desktop.in:%.desktop.in.in
|
||||||
|
$(AM_V_GEN) sed \
|
||||||
|
-e "s|\@libexecdir\@|$(libexecdir)|" \
|
||||||
|
-e "s|\@bindir\@|$(bindir)|" \
|
||||||
|
-e "s|\@VERSION\@|$(VERSION)|" \
|
||||||
|
$< > $@
|
||||||
|
|
||||||
|
comma:=,
|
||||||
|
empty:=
|
||||||
|
space:= $(empty) $(empty)
|
||||||
|
extensions:=$(patsubst %,\"%$(extensionbase)\",$(CLASSIC_EXTENSIONS))
|
||||||
|
extension_list:=$(subst $(space),$(comma),$(extensions))
|
||||||
|
%.json:%.json.in
|
||||||
|
$(AM_V_GEN) sed \
|
||||||
|
-e "s|\@CLASSIC_EXTENSIONS\@|$(extension_list)|g" \
|
||||||
|
$< > $@
|
||||||
|
|
||||||
|
%.session: %.session.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@
|
||||||
|
|
||||||
|
@INTLTOOL_DESKTOP_RULE@
|
||||||
|
|
||||||
|
EXTRA_DIST = \
|
||||||
|
$(desktop_in_in_files) \
|
||||||
|
$(session_in_in_files) \
|
||||||
|
$(xsession_in_files) \
|
||||||
|
$(mode_in_files) \
|
||||||
|
$(gsettings_in_files) \
|
||||||
|
$(theme_DATA) \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
CLEANFILES = \
|
||||||
|
$(desktop_DATA) \
|
||||||
|
$(session_DATA) \
|
||||||
|
$(xsession_DATA) \
|
||||||
|
$(mode_DATA) \
|
||||||
|
$(gsettings_SCHEMAS) \
|
||||||
|
$(NULL)
|
||||||
@@ -0,0 +1,695 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
id="svg5369"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.4 r9939"
|
||||||
|
width="96"
|
||||||
|
height="48"
|
||||||
|
sodipodi:docname="process-working-inverse.svg"
|
||||||
|
style="display:inline">
|
||||||
|
<metadata
|
||||||
|
id="metadata5375">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs5373">
|
||||||
|
<filter
|
||||||
|
id="filter3278"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3280"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3282"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3284"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3286"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3288"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3290"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3292"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3294"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3296"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3298"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3300"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3302"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3304"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3306"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3308"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3310"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3312"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3314"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3316"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3318"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3320"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3322"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3324"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3326"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3328"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3330"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3332"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3334"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3336"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
result="fbSourceGraphicAlpha"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
|
||||||
|
id="feColorMatrix3374" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3376"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic"
|
||||||
|
in="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3378"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3338"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3340"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3342"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
result="fbSourceGraphicAlpha"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
|
||||||
|
id="feColorMatrix3380" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3382"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic"
|
||||||
|
in="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3384"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3344"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3346"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3348"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
result="fbSourceGraphicAlpha"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
|
||||||
|
id="feColorMatrix3386" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3388"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic"
|
||||||
|
in="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3390"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3350"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3352"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3354"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
result="fbSourceGraphicAlpha"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
|
||||||
|
id="feColorMatrix3392" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3394"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic"
|
||||||
|
in="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3396"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3356"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3358"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3360"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
result="fbSourceGraphicAlpha"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
|
||||||
|
id="feColorMatrix3398" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3400"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic"
|
||||||
|
in="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3402"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3362"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3364"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3366"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
result="fbSourceGraphicAlpha"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
|
||||||
|
id="feColorMatrix3404" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3406"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic"
|
||||||
|
in="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3408"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="filter3368"
|
||||||
|
inkscape:label="Invert"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
inkscape:menu="Color"
|
||||||
|
inkscape:menu-tooltip="Invert colors"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3370"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3372"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
result="fbSourceGraphicAlpha"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
|
||||||
|
id="feColorMatrix3410" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3412"
|
||||||
|
type="saturate"
|
||||||
|
values="1"
|
||||||
|
result="fbSourceGraphic"
|
||||||
|
in="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="feColorMatrix3414"
|
||||||
|
in="fbSourceGraphic"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 " />
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#808080"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1364"
|
||||||
|
inkscape:window-height="708"
|
||||||
|
id="namedview5371"
|
||||||
|
showgrid="true"
|
||||||
|
borderlayer="true"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:zoom="5.27"
|
||||||
|
inkscape:cx="25.33344"
|
||||||
|
inkscape:cy="36.016983"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="layer1">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid11933"
|
||||||
|
empspacing="5"
|
||||||
|
visible="true"
|
||||||
|
enabled="true"
|
||||||
|
snapvisiblegridlinesonly="true" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
inkscape:label="tiles"
|
||||||
|
style="display:inline">
|
||||||
|
<rect
|
||||||
|
style="color:#000000;fill:#000000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;filter:url(#filter3278)"
|
||||||
|
id="rect12451"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
x="0"
|
||||||
|
y="0" />
|
||||||
|
<rect
|
||||||
|
y="24"
|
||||||
|
x="0"
|
||||||
|
height="24"
|
||||||
|
width="24"
|
||||||
|
id="rect12453"
|
||||||
|
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;filter:url(#filter3284);opacity:0" />
|
||||||
|
<rect
|
||||||
|
y="0"
|
||||||
|
x="24"
|
||||||
|
height="24"
|
||||||
|
width="24"
|
||||||
|
id="rect12455"
|
||||||
|
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;filter:url(#filter3290);opacity:0.00858369" />
|
||||||
|
<rect
|
||||||
|
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;filter:url(#filter3296);opacity:0"
|
||||||
|
id="rect12457"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
x="24"
|
||||||
|
y="24" />
|
||||||
|
<rect
|
||||||
|
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;filter:url(#filter3302);opacity:0"
|
||||||
|
id="rect12459"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
x="48"
|
||||||
|
y="0" />
|
||||||
|
<rect
|
||||||
|
y="24"
|
||||||
|
x="48"
|
||||||
|
height="24"
|
||||||
|
width="24"
|
||||||
|
id="rect12461"
|
||||||
|
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;filter:url(#filter3308);opacity:0" />
|
||||||
|
<rect
|
||||||
|
y="0"
|
||||||
|
x="72"
|
||||||
|
height="24"
|
||||||
|
width="24"
|
||||||
|
id="rect12463"
|
||||||
|
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;filter:url(#filter3314);opacity:0" />
|
||||||
|
<rect
|
||||||
|
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;filter:url(#filter3320);opacity:0"
|
||||||
|
id="rect12465"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
x="72"
|
||||||
|
y="24" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer2"
|
||||||
|
inkscape:label="spinner"
|
||||||
|
sodipodi:insensitive="true">
|
||||||
|
<g
|
||||||
|
transform="matrix(0.28240106,0,0,0.28240106,146.92015,-382.52444)"
|
||||||
|
id="g10450-5"
|
||||||
|
style="display:inline;filter:url(#filter3326)">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="opacity:0.6;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
d="m -477.76072,1373.3569 0,9.4717"
|
||||||
|
id="path18768"
|
||||||
|
sodipodi:nodetypes="cc"
|
||||||
|
inkscape:transform-center-y="-4.6808838" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:transform-center-y="-3.3099227"
|
||||||
|
sodipodi:nodetypes="cc"
|
||||||
|
id="path18770"
|
||||||
|
d="m -461.0171,1380.2922 -7.23427,7.3824"
|
||||||
|
style="opacity:0.7;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
inkscape:transform-center-x="-3.3098966" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:transform-center-x="-4.6808962"
|
||||||
|
style="opacity:0.8;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
d="m -454.08163,1397.0359 -9.47165,0"
|
||||||
|
id="path18772"
|
||||||
|
sodipodi:nodetypes="cc"
|
||||||
|
inkscape:transform-center-y="-2.6596956e-05" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc"
|
||||||
|
id="path18774"
|
||||||
|
d="m -461.01709,1413.7796 -6.93831,-7.0864"
|
||||||
|
style="opacity:0.9;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
inkscape:transform-center-x="-3.3098966"
|
||||||
|
inkscape:transform-center-y="3.3098652" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:transform-center-y="4.6808757"
|
||||||
|
style="color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
d="m -477.76074,1420.715 9e-5,-9.4716"
|
||||||
|
id="path18776"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc"
|
||||||
|
id="path18778"
|
||||||
|
d="m -494.50442,1413.7796 6.79048,-6.9384"
|
||||||
|
style="opacity:0.3;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
inkscape:transform-center-y="3.3098769"
|
||||||
|
inkscape:transform-center-x="3.3098883" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:transform-center-x="4.6808941"
|
||||||
|
style="opacity:0.4;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
d="m -501.43987,1397.0359 9.47174,0"
|
||||||
|
id="path18780"
|
||||||
|
sodipodi:nodetypes="cc"
|
||||||
|
inkscape:transform-center-y="-2.6596956e-05" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc"
|
||||||
|
id="path18782"
|
||||||
|
d="m -494.5044,1380.2922 6.64243,6.9384"
|
||||||
|
style="opacity:0.5;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
inkscape:transform-center-x="3.3098902"
|
||||||
|
inkscape:transform-center-y="-3.3099302" />
|
||||||
|
</g>
|
||||||
|
<use
|
||||||
|
style="display:inline;filter:url(#filter3332)"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
xlink:href="#g10450-5"
|
||||||
|
id="use4981"
|
||||||
|
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,36,-4.9705636)"
|
||||||
|
width="400"
|
||||||
|
height="400" />
|
||||||
|
<use
|
||||||
|
style="display:inline;filter:url(#filter3338)"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
xlink:href="#use4981"
|
||||||
|
id="use4983"
|
||||||
|
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,43.032478,-21.909695)"
|
||||||
|
width="400"
|
||||||
|
height="400" />
|
||||||
|
<use
|
||||||
|
style="display:inline;filter:url(#filter3344)"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
xlink:href="#use4983"
|
||||||
|
id="use4985"
|
||||||
|
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,50.081986,-38.904617)"
|
||||||
|
width="400"
|
||||||
|
height="400" />
|
||||||
|
<use
|
||||||
|
style="display:inline;filter:url(#filter3350)"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
xlink:href="#use4985"
|
||||||
|
id="use4987"
|
||||||
|
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,-38.919996,-31.872139)"
|
||||||
|
width="400"
|
||||||
|
height="400" />
|
||||||
|
<use
|
||||||
|
style="display:inline;filter:url(#filter3356)"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
xlink:href="#use4987"
|
||||||
|
id="use4989"
|
||||||
|
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,52.986628,2.0890543)"
|
||||||
|
width="400"
|
||||||
|
height="400" />
|
||||||
|
<use
|
||||||
|
style="display:inline;filter:url(#filter3362)"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
xlink:href="#use4989"
|
||||||
|
id="use4991"
|
||||||
|
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,60.013026,-14.912936)"
|
||||||
|
width="400"
|
||||||
|
height="400" />
|
||||||
|
<use
|
||||||
|
style="display:inline;filter:url(#filter3368)"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
xlink:href="#use4991"
|
||||||
|
id="use4993"
|
||||||
|
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,67.022396,-31.859127)"
|
||||||
|
width="400"
|
||||||
|
height="400" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,250 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="65"
|
||||||
|
height="22"
|
||||||
|
id="svg10865"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.4 r9939"
|
||||||
|
sodipodi:docname="toggle-off-us.svg">
|
||||||
|
<defs
|
||||||
|
id="defs10867">
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient62852-6-5"
|
||||||
|
id="linearGradient62981-1-1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.85639946,0,0,0.81059641,27.810405,92.069729)"
|
||||||
|
x1="740"
|
||||||
|
y1="486.10501"
|
||||||
|
x2="740"
|
||||||
|
y2="505.3204" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient62852-6-5">
|
||||||
|
<stop
|
||||||
|
id="stop62854-6-7"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#eeeeec;stop-opacity:1"
|
||||||
|
offset="0.97726452"
|
||||||
|
id="stop62858-8-0" />
|
||||||
|
<stop
|
||||||
|
id="stop62860-5-3"
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#f5f5f4;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
<inkscape:path-effect
|
||||||
|
is_visible="true"
|
||||||
|
id="path-effect62989-8-0"
|
||||||
|
effect="spiro" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient62821-5-8">
|
||||||
|
<stop
|
||||||
|
id="stop62823-2-4"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#d1d3d1;stop-opacity:1;" />
|
||||||
|
<stop
|
||||||
|
id="stop62825-3-8"
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#ebebeb;stop-opacity:1;" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient62852-6-5-3">
|
||||||
|
<stop
|
||||||
|
id="stop62854-6-7-6"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#eeeeec;stop-opacity:1"
|
||||||
|
offset="0.97726452"
|
||||||
|
id="stop62858-8-0-3" />
|
||||||
|
<stop
|
||||||
|
id="stop62860-5-3-9"
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#f5f5f4;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="spiro"
|
||||||
|
id="path-effect62829-6-8-0"
|
||||||
|
is_visible="true" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient62821-5-8-1"
|
||||||
|
id="linearGradient62864-0-3-1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.3066667,0,0,1,-197.64667,0)"
|
||||||
|
x1="702.58966"
|
||||||
|
y1="484.49405"
|
||||||
|
x2="702.58966"
|
||||||
|
y2="507.42715" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient62821-5-8-1">
|
||||||
|
<stop
|
||||||
|
id="stop62823-2-4-2"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#d1d3d1;stop-opacity:1;" />
|
||||||
|
<stop
|
||||||
|
id="stop62825-3-8-9"
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#ebebeb;stop-opacity:1;" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
y2="507.42715"
|
||||||
|
x2="702.58966"
|
||||||
|
y1="484.49405"
|
||||||
|
x1="702.58966"
|
||||||
|
gradientTransform="matrix(1.3066667,0,0,1,-841.64667,-483)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="linearGradient11647"
|
||||||
|
xlink:href="#linearGradient62821-5-8-1"
|
||||||
|
inkscape:collect="always" />
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="-133.20351"
|
||||||
|
inkscape:cy="20.84411"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="g62929"
|
||||||
|
showgrid="false"
|
||||||
|
borderlayer="true"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:snap-nodes="false"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:bbox-paths="true"
|
||||||
|
inkscape:snap-others="false"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1380"
|
||||||
|
inkscape:window-x="1600"
|
||||||
|
inkscape:window-y="28"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
showborder="false">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid11512" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata10870">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-1030.3622)">
|
||||||
|
<g
|
||||||
|
inkscape:export-ydpi="90"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/system-settings/network/network-panel-summary.png"
|
||||||
|
style="display:inline"
|
||||||
|
id="g62929"
|
||||||
|
transform="translate(-643.91421,517.29894)">
|
||||||
|
<g
|
||||||
|
transform="translate(0,30)"
|
||||||
|
id="g62931">
|
||||||
|
<rect
|
||||||
|
style="fill:url(#linearGradient62864-0-3-1);fill-opacity:1;stroke:#a6a8a6;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;color:#000000;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill-rule:nonzero;stroke-linejoin:miter;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero"
|
||||||
|
id="rect62935"
|
||||||
|
width="63.856125"
|
||||||
|
height="21.093594"
|
||||||
|
x="644.5"
|
||||||
|
y="483.5"
|
||||||
|
rx="2"
|
||||||
|
ry="2" />
|
||||||
|
<rect
|
||||||
|
style="fill:url(#linearGradient62981-1-1);fill-opacity:1;stroke:#777b7a;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||||
|
id="rect62937"
|
||||||
|
width="29.97398"
|
||||||
|
height="17.022524"
|
||||||
|
x="646.55896"
|
||||||
|
y="485.61429"
|
||||||
|
rx="1"
|
||||||
|
ry="1" />
|
||||||
|
<g
|
||||||
|
transform="translate(-38.048674,-1.9445437)"
|
||||||
|
id="g62939">
|
||||||
|
<rect
|
||||||
|
ry="1"
|
||||||
|
rx="1"
|
||||||
|
y="492"
|
||||||
|
x="695"
|
||||||
|
height="8"
|
||||||
|
width="2"
|
||||||
|
id="rect62941"
|
||||||
|
style="fill:#e8e8e5;fill-opacity:1;stroke:none;display:inline" />
|
||||||
|
<rect
|
||||||
|
ry="1"
|
||||||
|
rx="1"
|
||||||
|
y="492"
|
||||||
|
x="699"
|
||||||
|
height="8"
|
||||||
|
width="2"
|
||||||
|
id="rect62943"
|
||||||
|
style="fill:#e8e8e5;fill-opacity:1;stroke:none;display:inline" />
|
||||||
|
<rect
|
||||||
|
ry="1"
|
||||||
|
rx="1"
|
||||||
|
y="492"
|
||||||
|
x="703"
|
||||||
|
height="8"
|
||||||
|
width="2"
|
||||||
|
id="rect62945"
|
||||||
|
style="fill:#e8e8e5;fill-opacity:1;stroke:none;display:inline" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:original-d="m 647.78624,486.57192 27.94632,0"
|
||||||
|
inkscape:path-effect="#path-effect62989-8-0"
|
||||||
|
id="path62947"
|
||||||
|
d="m 647.78624,486.57192 27.94632,0"
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g11665"
|
||||||
|
transform="matrix(0.78906097,0,0,0.78906097,178.78814,111.57844)" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
transform="translate(642.41421,514.02037)"
|
||||||
|
d="m 55,11.875 a 4.625,4.625 0 1 1 -9.25,0 4.625,4.625 0 1 1 9.25,0 z"
|
||||||
|
sodipodi:ry="4.625"
|
||||||
|
sodipodi:rx="4.625"
|
||||||
|
sodipodi:cy="11.875"
|
||||||
|
sodipodi:cx="50.375"
|
||||||
|
id="path3786"
|
||||||
|
sodipodi:type="arc" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
id="path3016"
|
||||||
|
sodipodi:cx="50.375"
|
||||||
|
sodipodi:cy="11.875"
|
||||||
|
sodipodi:rx="4.625"
|
||||||
|
sodipodi:ry="4.625"
|
||||||
|
d="m 55,11.875 a 4.625,4.625 0 1 1 -9.25,0 4.625,4.625 0 1 1 9.25,0 z"
|
||||||
|
transform="translate(642.41421,512.02037)"
|
||||||
|
style="fill:none;stroke:#555753;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 8.1 KiB |
@@ -0,0 +1,255 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="65"
|
||||||
|
height="22"
|
||||||
|
id="svg10865"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48+devel r12050"
|
||||||
|
sodipodi:docname="toggle-off-us.svg">
|
||||||
|
<defs
|
||||||
|
id="defs10867">
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient62852-6-5"
|
||||||
|
id="linearGradient62981-1-1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.85639946,0,0,0.81059641,27.810405,92.069729)"
|
||||||
|
x1="740"
|
||||||
|
y1="486.10501"
|
||||||
|
x2="740"
|
||||||
|
y2="505.3204" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient62852-6-5">
|
||||||
|
<stop
|
||||||
|
id="stop62854-6-7"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#eeeeec;stop-opacity:1"
|
||||||
|
offset="0.97726452"
|
||||||
|
id="stop62858-8-0" />
|
||||||
|
<stop
|
||||||
|
id="stop62860-5-3"
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#f5f5f4;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
<inkscape:path-effect
|
||||||
|
is_visible="true"
|
||||||
|
id="path-effect62989-8-0"
|
||||||
|
effect="spiro" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient62821-5-8">
|
||||||
|
<stop
|
||||||
|
id="stop62823-2-4"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#d1d3d1;stop-opacity:1;" />
|
||||||
|
<stop
|
||||||
|
id="stop62825-3-8"
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#ebebeb;stop-opacity:1;" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient62852-6-5-3">
|
||||||
|
<stop
|
||||||
|
id="stop62854-6-7-6"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#eeeeec;stop-opacity:1"
|
||||||
|
offset="0.97726452"
|
||||||
|
id="stop62858-8-0-3" />
|
||||||
|
<stop
|
||||||
|
id="stop62860-5-3-9"
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#f5f5f4;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="spiro"
|
||||||
|
id="path-effect62829-6-8-0"
|
||||||
|
is_visible="true" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient62821-5-8-1"
|
||||||
|
id="linearGradient62864-0-3-1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.3066667,0,0,1,-197.64667,0)"
|
||||||
|
x1="702.58966"
|
||||||
|
y1="484.49405"
|
||||||
|
x2="702.58966"
|
||||||
|
y2="507.42715" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient62821-5-8-1">
|
||||||
|
<stop
|
||||||
|
id="stop62823-2-4-2"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#d1d3d1;stop-opacity:1;" />
|
||||||
|
<stop
|
||||||
|
id="stop62825-3-8-9"
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#ebebeb;stop-opacity:1;" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
y2="507.42715"
|
||||||
|
x2="702.58966"
|
||||||
|
y1="484.49405"
|
||||||
|
x1="702.58966"
|
||||||
|
gradientTransform="matrix(1.3066667,0,0,1,-841.64667,-483)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="linearGradient11647"
|
||||||
|
xlink:href="#linearGradient62821-5-8-1"
|
||||||
|
inkscape:collect="always" />
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="50.925774"
|
||||||
|
inkscape:cy="11.296867"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="g62929"
|
||||||
|
showgrid="false"
|
||||||
|
borderlayer="true"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:snap-nodes="false"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:bbox-paths="true"
|
||||||
|
inkscape:snap-others="false"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1375"
|
||||||
|
inkscape:window-x="1600"
|
||||||
|
inkscape:window-y="28"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
showborder="false">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid11512" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata10870">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-1030.3622)">
|
||||||
|
<g
|
||||||
|
inkscape:export-ydpi="90"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/system-settings/network/network-panel-summary.png"
|
||||||
|
style="display:inline"
|
||||||
|
id="g62929"
|
||||||
|
transform="translate(-643.91421,517.29894)">
|
||||||
|
<g
|
||||||
|
transform="translate(0,30)"
|
||||||
|
id="g62931">
|
||||||
|
<rect
|
||||||
|
style="fill:url(#linearGradient62864-0-3-1);fill-opacity:1;stroke:#a6a8a6;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;color:#000000;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill-rule:nonzero;stroke-linejoin:miter;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero"
|
||||||
|
id="rect62935"
|
||||||
|
width="63.856125"
|
||||||
|
height="21.093594"
|
||||||
|
x="644.5"
|
||||||
|
y="483.5"
|
||||||
|
rx="2"
|
||||||
|
ry="2" />
|
||||||
|
<rect
|
||||||
|
style="fill:url(#linearGradient62981-1-1);fill-opacity:1;stroke:#777b7a;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||||
|
id="rect62937"
|
||||||
|
width="29.97398"
|
||||||
|
height="17.022524"
|
||||||
|
x="646.55896"
|
||||||
|
y="485.61429"
|
||||||
|
rx="1"
|
||||||
|
ry="1" />
|
||||||
|
<g
|
||||||
|
transform="translate(-38.048674,-1.9445437)"
|
||||||
|
id="g62939">
|
||||||
|
<rect
|
||||||
|
ry="1"
|
||||||
|
rx="1"
|
||||||
|
y="492"
|
||||||
|
x="695"
|
||||||
|
height="8"
|
||||||
|
width="2"
|
||||||
|
id="rect62941"
|
||||||
|
style="fill:#e8e8e5;fill-opacity:1;stroke:none;display:inline" />
|
||||||
|
<rect
|
||||||
|
ry="1"
|
||||||
|
rx="1"
|
||||||
|
y="492"
|
||||||
|
x="699"
|
||||||
|
height="8"
|
||||||
|
width="2"
|
||||||
|
id="rect62943"
|
||||||
|
style="fill:#e8e8e5;fill-opacity:1;stroke:none;display:inline" />
|
||||||
|
<rect
|
||||||
|
ry="1"
|
||||||
|
rx="1"
|
||||||
|
y="492"
|
||||||
|
x="703"
|
||||||
|
height="8"
|
||||||
|
width="2"
|
||||||
|
id="rect62945"
|
||||||
|
style="fill:#e8e8e5;fill-opacity:1;stroke:none;display:inline" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:original-d="m 647.78624,486.57192 27.94632,0"
|
||||||
|
inkscape:path-effect="#path-effect62989-8-0"
|
||||||
|
id="path62947"
|
||||||
|
d="m 647.78624,486.57192 27.94632,0"
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g11665"
|
||||||
|
transform="matrix(0.78906097,0,0,0.78906097,167.78814,111.57844)">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:13.14816952px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;display:inline;font-family:Cantarell;-inkscape-font-specification:Cantarell Bold"
|
||||||
|
x="644.02277"
|
||||||
|
y="534.94739"
|
||||||
|
id="text62949-7"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(1.0113976,0.98873084)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan62951-7"
|
||||||
|
x="644.02277"
|
||||||
|
y="534.94739">OFF</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:12.69556618px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#2e3436;fill-opacity:1;stroke:none;font-family:Cantarell;-inkscape-font-specification:Cantarell Bold"
|
||||||
|
x="644.46393"
|
||||||
|
y="533.87158"
|
||||||
|
id="text62949"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(1.0113976,0.98873084)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan62951"
|
||||||
|
x="644.46393"
|
||||||
|
y="533.87158">OFF</tspan></text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 8.5 KiB |
@@ -0,0 +1,199 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="65"
|
||||||
|
height="22"
|
||||||
|
id="svg10865"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.4 r9939"
|
||||||
|
sodipodi:docname="toggle-on-us.svg">
|
||||||
|
<defs
|
||||||
|
id="defs10867">
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient62991-9-6"
|
||||||
|
id="linearGradient62979-8-1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.3066667,0,0,1,-197.64667,0)"
|
||||||
|
x1="702.58966"
|
||||||
|
y1="484.49405"
|
||||||
|
x2="702.58966"
|
||||||
|
y2="507.42715" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient62991-9-6">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#589fe9;stop-opacity:1"
|
||||||
|
offset="0"
|
||||||
|
id="stop62993-6-9" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#a3cefe;stop-opacity:1"
|
||||||
|
offset="1"
|
||||||
|
id="stop62995-1-6" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient62852-6-5"
|
||||||
|
id="linearGradient62981-1-1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.85639946,0,0,0.81059641,57.747905,92.132229)"
|
||||||
|
x1="740"
|
||||||
|
y1="486.10501"
|
||||||
|
x2="740"
|
||||||
|
y2="505.3204" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient62852-6-5">
|
||||||
|
<stop
|
||||||
|
id="stop62854-6-7"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#eeeeec;stop-opacity:1"
|
||||||
|
offset="0.97726452"
|
||||||
|
id="stop62858-8-0" />
|
||||||
|
<stop
|
||||||
|
id="stop62860-5-3"
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#f5f5f4;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
<inkscape:path-effect
|
||||||
|
is_visible="true"
|
||||||
|
id="path-effect62989-8-0"
|
||||||
|
effect="spiro" />
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="14.199007"
|
||||||
|
inkscape:cy="11.127758"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="g62929"
|
||||||
|
showgrid="false"
|
||||||
|
borderlayer="true"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:snap-nodes="false"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:bbox-paths="true"
|
||||||
|
inkscape:snap-others="false"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1380"
|
||||||
|
inkscape:window-x="1600"
|
||||||
|
inkscape:window-y="28"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
showborder="false">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid11512" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata10870">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-1030.3622)">
|
||||||
|
<g
|
||||||
|
inkscape:export-ydpi="90"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/system-settings/network/network-panel-summary.png"
|
||||||
|
style="display:inline"
|
||||||
|
id="g62929"
|
||||||
|
transform="translate(-643.91421,517.29894)">
|
||||||
|
<g
|
||||||
|
transform="translate(0,30)"
|
||||||
|
id="g62931">
|
||||||
|
<rect
|
||||||
|
style="fill:url(#linearGradient62979-8-1);fill-opacity:1;stroke:#1a71cc;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
id="rect62935"
|
||||||
|
width="63.856125"
|
||||||
|
height="21.093594"
|
||||||
|
x="644.5"
|
||||||
|
y="483.5"
|
||||||
|
rx="2"
|
||||||
|
ry="2" />
|
||||||
|
<rect
|
||||||
|
style="fill:url(#linearGradient62981-1-1);fill-opacity:1;stroke:#777b7a;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||||
|
id="rect62937"
|
||||||
|
width="29.97398"
|
||||||
|
height="17.022524"
|
||||||
|
x="676.49646"
|
||||||
|
y="485.67679"
|
||||||
|
rx="1"
|
||||||
|
ry="1" />
|
||||||
|
<g
|
||||||
|
transform="translate(-7.9861743,-1.9445437)"
|
||||||
|
id="g62939">
|
||||||
|
<rect
|
||||||
|
ry="1"
|
||||||
|
rx="1"
|
||||||
|
y="492"
|
||||||
|
x="695"
|
||||||
|
height="8"
|
||||||
|
width="2"
|
||||||
|
id="rect62941"
|
||||||
|
style="fill:#e8e8e5;fill-opacity:1;stroke:none;display:inline" />
|
||||||
|
<rect
|
||||||
|
ry="1"
|
||||||
|
rx="1"
|
||||||
|
y="492"
|
||||||
|
x="699"
|
||||||
|
height="8"
|
||||||
|
width="2"
|
||||||
|
id="rect62943"
|
||||||
|
style="fill:#e8e8e5;fill-opacity:1;stroke:none;display:inline" />
|
||||||
|
<rect
|
||||||
|
ry="1"
|
||||||
|
rx="1"
|
||||||
|
y="492"
|
||||||
|
x="703"
|
||||||
|
height="8"
|
||||||
|
width="2"
|
||||||
|
id="rect62945"
|
||||||
|
style="fill:#e8e8e5;fill-opacity:1;stroke:none;display:inline" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:original-d="m 677.59874,486.57192 27.94632,0"
|
||||||
|
inkscape:path-effect="#path-effect62989-8-0"
|
||||||
|
id="path62947"
|
||||||
|
d="m 677.59874,486.57192 27.94632,0"
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4444"
|
||||||
|
d="m 661.91421,522.09451 0,7"
|
||||||
|
style="fill:none;stroke:#1a71cc;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;color:#000000;fill-opacity:1;fill-rule:nonzero;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
d="m 18,7.96875 0,7"
|
||||||
|
id="path3922"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
transform="translate(643.91421,513.06326)" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.5 KiB |
@@ -0,0 +1,212 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="65"
|
||||||
|
height="22"
|
||||||
|
id="svg10865"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48+devel r12050"
|
||||||
|
sodipodi:docname="toggle-on-us.svg">
|
||||||
|
<defs
|
||||||
|
id="defs10867">
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient62991-9-6"
|
||||||
|
id="linearGradient62979-8-1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.3066667,0,0,1,-197.64667,0)"
|
||||||
|
x1="702.58966"
|
||||||
|
y1="484.49405"
|
||||||
|
x2="702.58966"
|
||||||
|
y2="507.42715" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient62991-9-6">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#589fe9;stop-opacity:1"
|
||||||
|
offset="0"
|
||||||
|
id="stop62993-6-9" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#a3cefe;stop-opacity:1"
|
||||||
|
offset="1"
|
||||||
|
id="stop62995-1-6" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient62852-6-5"
|
||||||
|
id="linearGradient62981-1-1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.85639946,0,0,0.81059641,57.747905,92.132229)"
|
||||||
|
x1="740"
|
||||||
|
y1="486.10501"
|
||||||
|
x2="740"
|
||||||
|
y2="505.3204" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient62852-6-5">
|
||||||
|
<stop
|
||||||
|
id="stop62854-6-7"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#eeeeec;stop-opacity:1"
|
||||||
|
offset="0.97726452"
|
||||||
|
id="stop62858-8-0" />
|
||||||
|
<stop
|
||||||
|
id="stop62860-5-3"
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#f5f5f4;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
<inkscape:path-effect
|
||||||
|
is_visible="true"
|
||||||
|
id="path-effect62989-8-0"
|
||||||
|
effect="spiro" />
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="8.2018252"
|
||||||
|
inkscape:cy="8.0650495"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="g62929"
|
||||||
|
showgrid="false"
|
||||||
|
borderlayer="true"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:snap-nodes="false"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:bbox-paths="true"
|
||||||
|
inkscape:snap-others="false"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1375"
|
||||||
|
inkscape:window-x="1600"
|
||||||
|
inkscape:window-y="28"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
showborder="false">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid11512" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata10870">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-1030.3622)">
|
||||||
|
<g
|
||||||
|
inkscape:export-ydpi="90"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/system-settings/network/network-panel-summary.png"
|
||||||
|
style="display:inline"
|
||||||
|
id="g62929"
|
||||||
|
transform="translate(-643.91421,517.29894)">
|
||||||
|
<g
|
||||||
|
transform="translate(0,30)"
|
||||||
|
id="g62931">
|
||||||
|
<rect
|
||||||
|
style="fill:url(#linearGradient62979-8-1);fill-opacity:1;stroke:#1a71cc;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
id="rect62935"
|
||||||
|
width="63.856125"
|
||||||
|
height="21.093594"
|
||||||
|
x="644.5"
|
||||||
|
y="483.5"
|
||||||
|
rx="2"
|
||||||
|
ry="2" />
|
||||||
|
<rect
|
||||||
|
style="fill:url(#linearGradient62981-1-1);fill-opacity:1;stroke:#777b7a;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||||
|
id="rect62937"
|
||||||
|
width="29.97398"
|
||||||
|
height="17.022524"
|
||||||
|
x="676.49646"
|
||||||
|
y="485.67679"
|
||||||
|
rx="1"
|
||||||
|
ry="1" />
|
||||||
|
<g
|
||||||
|
transform="translate(-7.9861743,-1.9445437)"
|
||||||
|
id="g62939">
|
||||||
|
<rect
|
||||||
|
ry="1"
|
||||||
|
rx="1"
|
||||||
|
y="492"
|
||||||
|
x="695"
|
||||||
|
height="8"
|
||||||
|
width="2"
|
||||||
|
id="rect62941"
|
||||||
|
style="fill:#e8e8e5;fill-opacity:1;stroke:none;display:inline" />
|
||||||
|
<rect
|
||||||
|
ry="1"
|
||||||
|
rx="1"
|
||||||
|
y="492"
|
||||||
|
x="699"
|
||||||
|
height="8"
|
||||||
|
width="2"
|
||||||
|
id="rect62943"
|
||||||
|
style="fill:#e8e8e5;fill-opacity:1;stroke:none;display:inline" />
|
||||||
|
<rect
|
||||||
|
ry="1"
|
||||||
|
rx="1"
|
||||||
|
y="492"
|
||||||
|
x="703"
|
||||||
|
height="8"
|
||||||
|
width="2"
|
||||||
|
id="rect62945"
|
||||||
|
style="fill:#e8e8e5;fill-opacity:1;stroke:none;display:inline" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:original-d="m 677.59874,486.57192 27.94632,0"
|
||||||
|
inkscape:path-effect="#path-effect62989-8-0"
|
||||||
|
id="path62947"
|
||||||
|
d="m 677.59874,486.57192 27.94632,0"
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
transform="scale(1.0113976,0.98873084)"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
id="text62949-7"
|
||||||
|
y="534.96918"
|
||||||
|
x="646.71399"
|
||||||
|
style="font-size:10.01104736px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#1a71cc;fill-opacity:1;stroke:none;display:inline;font-family:Cantarell;-inkscape-font-specification:Cantarell Bold"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
y="534.96918"
|
||||||
|
x="646.71399"
|
||||||
|
id="tspan62951-7"
|
||||||
|
sodipodi:role="line">ON</tspan></text>
|
||||||
|
<text
|
||||||
|
transform="scale(1.0113976,0.98873084)"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
id="text62949"
|
||||||
|
y="533.94482"
|
||||||
|
x="646.71399"
|
||||||
|
style="font-size:10.01104736px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Cantarell;-inkscape-font-specification:Cantarell Bold"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
y="533.94482"
|
||||||
|
x="646.71399"
|
||||||
|
id="tspan62951"
|
||||||
|
sodipodi:role="line">ON</tspan></text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.9 KiB |
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"parentMode": "user",
|
"parentMode": "user",
|
||||||
"colorScheme": "force-light",
|
"stylesheetName": "gnome-classic.css",
|
||||||
"hasOverview": false,
|
"overridesSchema": "org.gnome.shell.extensions.classic-overrides",
|
||||||
"showWelcomeDialog": false,
|
|
||||||
"enabledExtensions": [@CLASSIC_EXTENSIONS@],
|
"enabledExtensions": [@CLASSIC_EXTENSIONS@],
|
||||||
"panel": { "left": [],
|
"panel": { "left": ["activities", "appMenu"],
|
||||||
"center": [],
|
"center": [],
|
||||||
"right": ["a11y", "keyboard", "dateMenu", "quickSettings"]
|
"right": ["a11y", "keyboard", "volume", "bluetooth",
|
||||||
|
"network", "battery", "dateMenu", "userMenu"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Name=GNOME Classic on Wayland
|
|
||||||
Comment=This session logs you into GNOME Classic
|
|
||||||
Exec=env GNOME_SHELL_SESSION_MODE=classic gnome-session
|
|
||||||
TryExec=gnome-session
|
|
||||||
Type=Application
|
|
||||||
DesktopNames=GNOME-Classic;GNOME;
|
|
||||||
X-GDM-SessionRegisters=true
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Name=GNOME Classic on Xorg
|
|
||||||
Comment=This session logs you into GNOME Classic
|
|
||||||
Exec=env GNOME_SHELL_SESSION_MODE=classic gnome-session
|
|
||||||
TryExec=gnome-session
|
|
||||||
Type=Application
|
|
||||||
DesktopNames=GNOME-Classic;GNOME;
|
|
||||||
X-GDM-SessionRegisters=true
|
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
@import url("gnome-shell.css");
|
||||||
|
|
||||||
|
/* FIXME:
|
||||||
|
- white edge highlight with text-shadow and icon-shadow for panel-button
|
||||||
|
- better shading of the panel (dark 5%) - impossible without multipoint gradients, image-bg is a hack
|
||||||
|
*/
|
||||||
|
|
||||||
|
#panel {
|
||||||
|
background-color: #e9e9e9 !important;
|
||||||
|
background-gradient-direction: vertical;
|
||||||
|
background-gradient-end: #d0d0d0;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* hrm, still no multipoint gradients
|
||||||
|
background-image: linear-gradient(left, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1) 50%, rgba(255, 255, 255, 0)) !important;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#panel:overview {
|
||||||
|
background-color: #000 !important;
|
||||||
|
background-gradient-end: #000 !important;
|
||||||
|
border-top-color: #000;
|
||||||
|
border-bottom: 1px solid #000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#panel.lock-screen {
|
||||||
|
background-color: rgba(0,0,0,0.3) !important;
|
||||||
|
background-gradient-end: rgba(0,0,0,0.3) !important;
|
||||||
|
border-top-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#panel.unlock-screen {
|
||||||
|
background-color: transparent !important;
|
||||||
|
background-gradient-end: transparent !important;
|
||||||
|
border-top-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* TOP BAR */
|
||||||
|
|
||||||
|
.panel-corner,
|
||||||
|
.panel-corner:active,
|
||||||
|
.panel-corner:overview,
|
||||||
|
.panel-corner:focus {
|
||||||
|
-panel-corner-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-button {
|
||||||
|
color: #555 !important;
|
||||||
|
-natural-hpadding: 6px !important;
|
||||||
|
-minimum-hpadding: 3px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#panel:overview .panel-button,
|
||||||
|
#panel.lock-screen .panel-button,
|
||||||
|
#panel.unlock-screen .panel-button {
|
||||||
|
color: #ccc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-button:hover {
|
||||||
|
color: #000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#panel:overview .panel-button:hover,
|
||||||
|
#panel:overview .panel-button:active {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-button > .system-status-icon {
|
||||||
|
icon-shadow: #fff 0 1px 0 !important; /* FIXME */
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-button:hover,
|
||||||
|
.panel-button:active,
|
||||||
|
.panel-button:overview,
|
||||||
|
.panel-button:focus {
|
||||||
|
text-shadow: 0 0 0 transparent !important; /* FIXME: why can't I do none ? */
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-button:active,
|
||||||
|
.panel-button:overview,
|
||||||
|
.panel-button:focus {
|
||||||
|
background-color: #4a90d9 !important; /* FIXME */
|
||||||
|
color: #fff !important;
|
||||||
|
border: none !important;
|
||||||
|
border-image: none !important;
|
||||||
|
background-image: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#panelUserMenu {
|
||||||
|
padding: 0 10px 0 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#appMenu {
|
||||||
|
text-shadow: 0 0 0 transparent !important;
|
||||||
|
spinner-image: url("classic-process-working.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* used for the app menu header only */
|
||||||
|
.label-shadow {
|
||||||
|
color: rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-shadow {
|
||||||
|
color: rgba(255,255,255,.5) !important;
|
||||||
|
}
|
||||||
|
.panel-button:active .label-shadow,
|
||||||
|
.panel-button:focus .label-shadow {
|
||||||
|
color: rgba(0,0,0,.5) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-menu-boxpointer {
|
||||||
|
-arrow-background-color: #e9e9e9 !important;
|
||||||
|
-arrow-border-width: 1px !important;
|
||||||
|
-arrow-border-color: #aaa !important;
|
||||||
|
-arrow-border-radius: 3px !important;
|
||||||
|
color: #000 !important;
|
||||||
|
-arrow-base: 11px !important;
|
||||||
|
-arrow-rise: 5px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-combo-menu {
|
||||||
|
background-color: #e9e9e9 !important;
|
||||||
|
border: 1px solid #aaa !important;
|
||||||
|
color: #000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-menu-item:active {
|
||||||
|
background-color: #4a90d9 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-menu-item:insensitive {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-separator-menu-item {
|
||||||
|
-gradient-height: 1px;
|
||||||
|
-gradient-start: rgba(0,0,0,0.0);
|
||||||
|
-gradient-end: rgba(0,0,0,0.15);
|
||||||
|
-margin-horizontal: 24px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 8px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-slider-menu-item {
|
||||||
|
-slider-handle-border-color: #888;
|
||||||
|
-slider-handle-border-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-menu-item:hover .popup-slider-menu-item {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-subtitle-menu-item, .popup-subtitle-menu-item:insensitive {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-submenu-menu-item:open {
|
||||||
|
background-color: #888;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-sub-menu {
|
||||||
|
background-gradient-start: #ddd;
|
||||||
|
background-gradient-end: #dfdfdf;
|
||||||
|
background-gradient-direction: vertical;
|
||||||
|
box-shadow: inset 0px 1px 3px rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SCROLLBARS for POPUP MENU */
|
||||||
|
|
||||||
|
.popup-menu StScrollBar StBin#trough {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-menu StScrollBar StButton#vhandle, .popup-menu StScrollBar StButton#hhandle {
|
||||||
|
border-color: #e9e9e9;
|
||||||
|
background-color: #888;
|
||||||
|
}
|
||||||
|
.popup-menu StScrollBar StButton#vhandle:hover,
|
||||||
|
.popup-menu StScrollBar StButton#hhandle:hover {
|
||||||
|
background-color: #666;
|
||||||
|
}
|
||||||
|
.popup-menu StScrollBar StButton#vhandle:active,
|
||||||
|
.popup-menu StScrollBar StButton#hhandle:active {
|
||||||
|
background-color: #4a90d9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CALENDAR */
|
||||||
|
|
||||||
|
.calendar {}
|
||||||
|
.calendar-month-label {
|
||||||
|
color: #333 !important;
|
||||||
|
}
|
||||||
|
.datemenu-date-label,
|
||||||
|
.events-day-header {
|
||||||
|
color: #666 !important;
|
||||||
|
}
|
||||||
|
.calendar-day-base:active {
|
||||||
|
color: #fff !important;
|
||||||
|
background-color: #4a90d9 !important;
|
||||||
|
background-image: none !important;
|
||||||
|
border-image: none !important;
|
||||||
|
}
|
||||||
|
.calendar-today {
|
||||||
|
background-color: #4a90d9 !important;
|
||||||
|
background-image: none !important;
|
||||||
|
border-image: none !important;
|
||||||
|
}
|
||||||
|
.calendar-day-base {
|
||||||
|
color: #666 !important;
|
||||||
|
}
|
||||||
|
.calendar-day-base:hover {
|
||||||
|
background-color: #666 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.calendar-nonwork-day {
|
||||||
|
color: #999 !important;
|
||||||
|
}
|
||||||
|
.events-day-dayname,
|
||||||
|
.events-day-time {
|
||||||
|
color: #666 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* VOLUME SLIDER */
|
||||||
|
|
||||||
|
.popup-slider-menu-item {
|
||||||
|
-slider-background-color: #e9e9e9;
|
||||||
|
-slider-border-color: #999;
|
||||||
|
-slider-active-background-color: #76b0ec;
|
||||||
|
-slider-active-border-color: #1f6dbc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ON OFF switch */
|
||||||
|
|
||||||
|
.toggle-switch-us {
|
||||||
|
background-image: url("classic-toggle-off-us.svg");
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
.toggle-switch-us:checked {
|
||||||
|
background-image: url("classic-toggle-on-us.svg");
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch-intl {
|
||||||
|
background-image: url("classic-toggle-off-intl.svg");
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
.toggle-switch-intl:checked {
|
||||||
|
background-image: url("classic-toggle-on-intl.svg");
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Name=GNOME Classic
|
_Name=GNOME Classic
|
||||||
Comment=This session logs you into GNOME Classic
|
_Comment=This session logs you into GNOME Classic
|
||||||
Exec=env GNOME_SHELL_SESSION_MODE=classic gnome-session
|
Exec=gnome-session --session gnome-classic
|
||||||
TryExec=gnome-session
|
TryExec=gnome-session
|
||||||
|
Icon=
|
||||||
Type=Application
|
Type=Application
|
||||||
DesktopNames=GNOME-Classic;GNOME;
|
|
||||||
X-GDM-SessionRegisters=true
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[GNOME Session]
|
||||||
|
_Name=GNOME Classic
|
||||||
|
RequiredComponents=gnome-shell-classic;gnome-settings-daemon;nautilus-classic;
|
||||||
|
IsRunnableHelper=@libexecdir@/gnome-session-check-accelerated
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
_Name=GNOME Shell Classic
|
||||||
|
_Comment=Window management and application launching
|
||||||
|
Exec=@bindir@/gnome-shell --mode=classic
|
||||||
|
TryExec=@bindir@/gnome-shell
|
||||||
|
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||||
|
X-GNOME-Bugzilla-Product=gnome-shell
|
||||||
|
X-GNOME-Bugzilla-Component=general
|
||||||
|
X-GNOME-Bugzilla-Version=@VERSION@
|
||||||
|
Categories=GNOME;GTK;Core;
|
||||||
|
OnlyShowIn=GNOME;
|
||||||
|
NoDisplay=true
|
||||||
|
X-GNOME-Autostart-Phase=WindowManager
|
||||||
|
X-GNOME-Provides=panel;windowmanager;
|
||||||
|
X-GNOME-Autostart-Notify=true
|
||||||
|
X-GNOME-AutoRestart=false
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
session_desktop_base = 'gnome-classic'
|
|
||||||
|
|
||||||
session_desktops = [
|
|
||||||
session_desktop_base,
|
|
||||||
session_desktop_base + '-xorg',
|
|
||||||
session_desktop_base + '-wayland',
|
|
||||||
]
|
|
||||||
|
|
||||||
foreach name: session_desktops
|
|
||||||
session_desktop = name + '.desktop'
|
|
||||||
if name.endswith('-xorg')
|
|
||||||
session_instdir = xsessiondir
|
|
||||||
elif name.endswith('-wayland')
|
|
||||||
session_instdir = wlsessiondir
|
|
||||||
else
|
|
||||||
# FIXME: The same target can not be copied into two directories.
|
|
||||||
# There is a workaround in meson/session-post-install.py until proper
|
|
||||||
# solution arises:
|
|
||||||
# https://github.com/mesonbuild/meson/issues/2416
|
|
||||||
session_instdir = xsessiondir
|
|
||||||
#session_instdir = [ xesssiondir, wlsessiondir ]
|
|
||||||
endif
|
|
||||||
i18n.merge_file(
|
|
||||||
input: session_desktop + '.in',
|
|
||||||
output: session_desktop,
|
|
||||||
po_dir: '../po',
|
|
||||||
install: true,
|
|
||||||
install_dir: session_instdir,
|
|
||||||
type: 'desktop'
|
|
||||||
)
|
|
||||||
endforeach
|
|
||||||
|
|
||||||
classic_uuids = []
|
|
||||||
foreach e : classic_extensions
|
|
||||||
classic_uuids += e + uuid_suffix
|
|
||||||
endforeach
|
|
||||||
|
|
||||||
mode_conf = configuration_data()
|
|
||||||
mode_conf.set('CLASSIC_EXTENSIONS', '"' + '", "'.join(classic_uuids) + '"')
|
|
||||||
|
|
||||||
mode_file = 'classic.json'
|
|
||||||
configure_file(
|
|
||||||
input: mode_file + '.in',
|
|
||||||
output: mode_file,
|
|
||||||
configuration: mode_conf,
|
|
||||||
install_dir: modedir
|
|
||||||
)
|
|
||||||
|
|
||||||
classic_override = '00_org.gnome.shell.extensions.classic.gschema.override'
|
|
||||||
install_data(classic_override, install_dir: schemadir)
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<schemalist>
|
||||||
|
<schema id="org.gnome.shell.extensions.classic-overrides"
|
||||||
|
path="/org/gnome/shell/extensions/classic-overrides/"
|
||||||
|
gettext-domain="gnome-shell-extensions">
|
||||||
|
<key name="attach-modal-dialogs" type="b">
|
||||||
|
<default>true</default>
|
||||||
|
<_summary>Attach modal dialog to the parent window</_summary>
|
||||||
|
<_description>
|
||||||
|
This key overrides the key in org.gnome.mutter when running
|
||||||
|
GNOME Shell.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
|
|
||||||
|
<key name="edge-tiling" type="b">
|
||||||
|
<default>true</default>
|
||||||
|
<_summary>Enable edge tiling when dropping windows on screen edges</_summary>
|
||||||
|
<_description>
|
||||||
|
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
|
|
||||||
|
<key name="workspaces-only-on-primary" type="b">
|
||||||
|
<default>true</default>
|
||||||
|
<_summary>Workspaces only on primary monitor</_summary>
|
||||||
|
<_description>
|
||||||
|
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
|
|
||||||
|
<key name="focus-change-on-pointer-rest" type="b">
|
||||||
|
<default>true</default>
|
||||||
|
<summary>Delay focus changes in mouse mode until the pointer stops moving</summary>
|
||||||
|
<description>
|
||||||
|
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||||
|
</description>
|
||||||
|
</key>
|
||||||
|
</schema>
|
||||||
|
</schemalist>
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
srcdir=`dirname $0`
|
|
||||||
srcdir=`(cd $srcdir && pwd)`
|
|
||||||
|
|
||||||
builddir=`mktemp -p $srcdir -d _build.XXXXXX` || exit 1
|
|
||||||
installdir=`mktemp -p $srcdir -d _install.XXXXXX` || exit 1
|
|
||||||
|
|
||||||
meson setup --prefix=$installdir -Dextension_set=all $srcdir $builddir
|
|
||||||
meson install -C $builddir
|
|
||||||
|
|
||||||
rm -rf $srcdir/zip-files
|
|
||||||
mkdir $srcdir/zip-files
|
|
||||||
|
|
||||||
extensiondir=$installdir/share/gnome-shell/extensions
|
|
||||||
schemadir=$installdir/share/glib-2.0/schemas
|
|
||||||
|
|
||||||
for f in $extensiondir/*; do
|
|
||||||
name=`basename ${f%%@*}`
|
|
||||||
uuid=$name@gnome-shell-extensions.gcampax.github.com
|
|
||||||
schema=$schemadir/org.gnome.shell.extensions.$name.gschema.xml
|
|
||||||
|
|
||||||
olddomain=gnome-shell-extensions
|
|
||||||
newdomain=gnome-shell-extension-$name
|
|
||||||
sed -i "/gettext-domain/ s:$olddomain:$newdomain:" $f/metadata.json
|
|
||||||
|
|
||||||
xgettext --from-code=UTF-8 --output-dir=$builddir --output=$name.pot $f/*.js
|
|
||||||
|
|
||||||
if [ -f $builddir/$name.pot ]; then
|
|
||||||
mkdir $f/po
|
|
||||||
for l in $(<$srcdir/po/LINGUAS); do
|
|
||||||
msgmerge --quiet --output-file=$f/po/$l.po \
|
|
||||||
$srcdir/po/$l.po $builddir/$name.pot
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
cp $srcdir/NEWS $srcdir/COPYING $f
|
|
||||||
sources=(NEWS COPYING $(cd $f; ls *.js))
|
|
||||||
|
|
||||||
[ -f $schema ] || unset schema
|
|
||||||
|
|
||||||
gnome-extensions pack ${sources[@]/#/--extra-source=} \
|
|
||||||
${schema:+--schema=$schema} --out-dir=$srcdir/zip-files $f
|
|
||||||
done
|
|
||||||
|
|
||||||
rm -rf $builddir
|
|
||||||
rm -rf $installdir
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
include $(top_srcdir)/include.mk
|
||||||
|
|
||||||
|
dist_extension_DATA = extension.js stylesheet.css $(EXTRA_MODULES)
|
||||||
|
nodist_extension_DATA = metadata.json $(top_srcdir)/lib/convenience.js $(EXTRA_EXTENSION)
|
||||||
|
|
||||||
|
EXTRA_DIST = metadata.json.in
|
||||||
|
|
||||||
|
metadata.json: metadata.json.in $(top_builddir)/config.status
|
||||||
|
$(AM_V_GEN) sed \
|
||||||
|
-e "s|[@]extension_id@|$(EXTENSION_ID)|" \
|
||||||
|
-e "s|[@]uuid@|$(uuid)|" \
|
||||||
|
-e "s|[@]gschemaname@|$(gschemaname)|" \
|
||||||
|
-e "s|[@]gettext_domain@|$(GETTEXT_PACKAGE)|" \
|
||||||
|
-e "s|[@]shell_current@|$(SHELL_VERSION)|" \
|
||||||
|
-e "s|[@]url@|$(extensionurl)|" \
|
||||||
|
$< > $@
|
||||||
|
|
||||||
|
CLEANFILES = metadata.json
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
DIST_SUBDIRS = $(ALL_EXTENSIONS)
|
||||||
|
|
||||||
|
SUBDIRS = $(ENABLED_EXTENSIONS)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
EXTENSION_ID = alternate-tab
|
||||||
|
|
||||||
|
EXTRA_MODULES = prefs.js
|
||||||
|
|
||||||
|
include ../../extension.mk
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/* -*- mode: js; js-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const AltTab = imports.ui.altTab;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
|
let injections = {};
|
||||||
|
|
||||||
|
function init(metadata) {
|
||||||
|
}
|
||||||
|
|
||||||
|
function setKeybinding(name, func) {
|
||||||
|
Main.wm.setCustomKeybindingHandler(name, Shell.KeyBindingMode.NORMAL, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
function enable() {
|
||||||
|
injections['_initialSelection'] = AltTab.WindowSwitcherPopup.prototype._initialSelection;
|
||||||
|
AltTab.WindowSwitcherPopup.prototype._initialSelection = function(backward, binding) {
|
||||||
|
if (binding == 'switch-windows-backward' ||
|
||||||
|
binding == 'switch-applications-backward' ||
|
||||||
|
binding == 'switch-group-backward' || backward)
|
||||||
|
this._select(this._items.length - 1);
|
||||||
|
else if (this._items.length == 1)
|
||||||
|
this._select(0);
|
||||||
|
else
|
||||||
|
this._select(1);
|
||||||
|
};
|
||||||
|
injections['_keyPressHandler'] = AltTab.WindowSwitcherPopup.prototype._keyPressHandler;
|
||||||
|
AltTab.WindowSwitcherPopup.prototype._keyPressHandler = function(keysym, backwards, action) {
|
||||||
|
if (action == Meta.KeyBindingAction.SWITCH_WINDOWS ||
|
||||||
|
action == Meta.KeyBindingAction.SWITCH_APPLICATIONS ||
|
||||||
|
action == Meta.KeyBindingAction.SWITCH_GROUP) {
|
||||||
|
this._select(backwards ? this._previous() : this._next());
|
||||||
|
} else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD ||
|
||||||
|
action == Meta.KeyBindingAction.SWITCH_APPLICATIONS_BACKWARD ||
|
||||||
|
action == Meta.KeyBindingAction.SWITCH_GROUP_BACKWARD) {
|
||||||
|
this._select(this._previous());
|
||||||
|
} else {
|
||||||
|
if (keysym == Clutter.Left)
|
||||||
|
this._select(this._previous());
|
||||||
|
else if (keysym == Clutter.Right)
|
||||||
|
this._select(this._next());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setKeybinding('switch-applications', Lang.bind(Main.wm, Main.wm._startWindowSwitcher));
|
||||||
|
setKeybinding('switch-group', Lang.bind(Main.wm, Main.wm._startWindowSwitcher));
|
||||||
|
setKeybinding('switch-applications-backward', Lang.bind(Main.wm, Main.wm._startWindowSwitcher));
|
||||||
|
setKeybinding('switch-group-backward', Lang.bind(Main.wm, Main.wm._startWindowSwitcher));
|
||||||
|
}
|
||||||
|
|
||||||
|
function disable() {
|
||||||
|
setKeybinding('switch-applications', Lang.bind(Main.wm, Main.wm._startAppSwitcher));
|
||||||
|
setKeybinding('switch-group', Lang.bind(Main.wm, Main.wm._startAppSwitcher));
|
||||||
|
setKeybinding('switch-applications-backward', Lang.bind(Main.wm, Main.wm._startAppSwitcher));
|
||||||
|
setKeybinding('switch-group-backward', Lang.bind(Main.wm, Main.wm._startAppSwitcher));
|
||||||
|
|
||||||
|
for (prop in injections)
|
||||||
|
AltTab.WindowSwitcherPopup.prototype[prop] = injections[prop];
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"extension-id": "@extension_id@",
|
||||||
|
"uuid": "@uuid@",
|
||||||
|
"settings-schema": "@gschemaname@",
|
||||||
|
"gettext-domain": "@gettext_domain@",
|
||||||
|
"name": "AlternateTab",
|
||||||
|
"description": "A replacement for Alt-Tab, allows to cycle between windows and does not group by application",
|
||||||
|
"original-authors": [ "jw@bargsten.org", "thomas.bouffon@gmail.com" ],
|
||||||
|
"shell-version": [ "@shell_current@" ],
|
||||||
|
"url": "@url@"
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const GObject = imports.gi.GObject;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
|
||||||
|
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
const N_ = function(e) { return e };
|
||||||
|
|
||||||
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
|
const Convenience = Me.imports.convenience;
|
||||||
|
|
||||||
|
const SETTINGS_APP_ICON_MODE = 'app-icon-mode';
|
||||||
|
const SETTINGS_CURRENT_WORKSPACE_ONLY = 'current-workspace-only';
|
||||||
|
|
||||||
|
const MODES = {
|
||||||
|
'thumbnail-only': N_("Thumbnail only"),
|
||||||
|
'app-icon-only': N_("Application icon only"),
|
||||||
|
'both': N_("Thumbnail and application icon"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const AltTabSettingsWidget = new GObject.Class({
|
||||||
|
Name: 'AlternateTab.Prefs.AltTabSettingsWidget',
|
||||||
|
GTypeName: 'AltTabSettingsWidget',
|
||||||
|
Extends: Gtk.Grid,
|
||||||
|
|
||||||
|
_init : function(params) {
|
||||||
|
this.parent(params);
|
||||||
|
this.margin = 10;
|
||||||
|
this.orientation = Gtk.Orientation.VERTICAL;
|
||||||
|
|
||||||
|
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
|
||||||
|
|
||||||
|
let presentLabel = _("Present windows as");
|
||||||
|
this.add(new Gtk.Label({ label: presentLabel, sensitive: true,
|
||||||
|
margin_bottom: 10, margin_top: 5 }));
|
||||||
|
|
||||||
|
let top = 1;
|
||||||
|
let radio = null;
|
||||||
|
let currentMode = this._settings.get_string(SETTINGS_APP_ICON_MODE);
|
||||||
|
for (let mode in MODES) {
|
||||||
|
// copy the mode variable because it has function scope, not block scope
|
||||||
|
// so cannot be used in a closure
|
||||||
|
let modeCapture = mode;
|
||||||
|
let name = Gettext.gettext(MODES[mode]);
|
||||||
|
|
||||||
|
radio = new Gtk.RadioButton({ group: radio, label: name, valign: Gtk.Align.START });
|
||||||
|
radio.connect('toggled', Lang.bind(this, function(widget) {
|
||||||
|
if (widget.active)
|
||||||
|
this._settings.set_string(SETTINGS_APP_ICON_MODE, modeCapture);
|
||||||
|
}));
|
||||||
|
this.add(radio);
|
||||||
|
|
||||||
|
if (mode == currentMode)
|
||||||
|
radio.active = true;
|
||||||
|
top += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let check = new Gtk.CheckButton({ label: _("Show only windows in the current workspace"),
|
||||||
|
margin_top: 12 });
|
||||||
|
this._settings.bind(SETTINGS_CURRENT_WORKSPACE_ONLY, check, 'active', Gio.SettingsBindFlags.DEFAULT);
|
||||||
|
this.add(check);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
Convenience.initTranslations();
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPrefsWidget() {
|
||||||
|
let widget = new AltTabSettingsWidget();
|
||||||
|
widget.show_all();
|
||||||
|
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/* This extensions requires no special styling */
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
EXTENSION_ID = alternative-status-menu
|
||||||
|
|
||||||
|
include ../../extension.mk
|
||||||
|
include ../../settings.mk
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
/* -*- mode: js2 - indent-tabs-mode: nil - js2-basic-offset: 4 -*- */
|
||||||
|
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const BoxPointer = imports.ui.boxpointer;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
|
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
|
||||||
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
|
const Convenience = Me.imports.convenience;
|
||||||
|
|
||||||
|
const LOCK_ENABLED_KEY = 'lock-enabled';
|
||||||
|
|
||||||
|
let extension;
|
||||||
|
|
||||||
|
// Need to reimplement here the missing bits from LoginManager
|
||||||
|
|
||||||
|
function loginManager_hibernate() {
|
||||||
|
if (this._proxy) {
|
||||||
|
// systemd path
|
||||||
|
this._proxy.call("Hibernate",
|
||||||
|
GLib.Variant.new('(b)', [true]),
|
||||||
|
Gio.DBusCallFlags.NONE,
|
||||||
|
-1, null, null);
|
||||||
|
} else {
|
||||||
|
// Can't do in ConsoleKit
|
||||||
|
this.emit('prepare-for-sleep', true);
|
||||||
|
this.emit('prepare-for-sleep', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loginManager_canHibernate(asyncCallback) {
|
||||||
|
if (this._proxy) {
|
||||||
|
// systemd path
|
||||||
|
this._proxy.call("CanHibernate",
|
||||||
|
null,
|
||||||
|
Gio.DBusCallFlags.NONE,
|
||||||
|
-1, null, function(proxy, asyncResult) {
|
||||||
|
let result, error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = proxy.call_finish(asyncResult);
|
||||||
|
} catch(e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
asyncCallback(false);
|
||||||
|
else
|
||||||
|
asyncCallback(result[0] != 'no');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Mainloop.idle_add(Lang.bind(this, function() {
|
||||||
|
asyncCallback(false);
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusMenu_updateHaveHibernate() {
|
||||||
|
loginManager_canHibernate.call(this._loginManager, Lang.bind(this,
|
||||||
|
function(result) {
|
||||||
|
this._haveHibernate = result;
|
||||||
|
this._updateSuspendOrPowerOff();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusMenu_updateSuspendOrPowerOff() {
|
||||||
|
this._suspendOrPowerOffItem.actor.hide();
|
||||||
|
|
||||||
|
extension.suspendItem.actor.visible = this._haveSuspend && extension.settings.get_boolean('allow-suspend');
|
||||||
|
extension.hibernateItem.actor.visible = this._haveHibernate && extension.settings.get_boolean('allow-hibernate');
|
||||||
|
extension.powerOffItem.actor.visible = this._haveShutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSuspendActivate(item) {
|
||||||
|
Main.overview.hide();
|
||||||
|
|
||||||
|
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||||
|
this._loginManager.suspend();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onHibernateActivate(item) {
|
||||||
|
Main.overview.hide();
|
||||||
|
|
||||||
|
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||||
|
loginManager_hibernate.call(this._loginManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Extension = new Lang.Class({
|
||||||
|
Name: 'AlternativeStatusMenu.Extension',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.suspendItem = null;
|
||||||
|
this.hibernateItem = null;
|
||||||
|
this.powerOffItem = null;
|
||||||
|
|
||||||
|
Convenience.initTranslations();
|
||||||
|
this.settings = Convenience.getSettings();
|
||||||
|
},
|
||||||
|
|
||||||
|
enable: function() {
|
||||||
|
let statusMenu = Main.panel.statusArea.userMenu;
|
||||||
|
|
||||||
|
let children = statusMenu.menu._getMenuItems();
|
||||||
|
let index = children.length;
|
||||||
|
|
||||||
|
/* find the old entry */
|
||||||
|
for (let i = children.length - 1; i >= 0; i--) {
|
||||||
|
if (children[i] == statusMenu._suspendOrPowerOffItem) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add the new entries */
|
||||||
|
this.suspendItem = new PopupMenu.PopupMenuItem(_("Suspend"));
|
||||||
|
this.suspendItem.connect('activate', Lang.bind(statusMenu, onSuspendActivate));
|
||||||
|
|
||||||
|
this.hibernateItem = new PopupMenu.PopupMenuItem(_("Hibernate"));
|
||||||
|
this.hibernateItem.connect('activate', Lang.bind(statusMenu, onHibernateActivate));
|
||||||
|
|
||||||
|
this.powerOffItem = new PopupMenu.PopupMenuItem(_("Power Off"));
|
||||||
|
this.powerOffItem.connect('activate', Lang.bind(statusMenu, function() {
|
||||||
|
this._session.ShutdownRemote();
|
||||||
|
}));
|
||||||
|
|
||||||
|
/* insert the entries at the found position */
|
||||||
|
statusMenu.menu.addMenuItem(this.suspendItem, index);
|
||||||
|
statusMenu.menu.addMenuItem(this.hibernateItem, index + 1);
|
||||||
|
statusMenu.menu.addMenuItem(this.powerOffItem, index + 2);
|
||||||
|
|
||||||
|
this._openStateChangedId = statusMenu.menu.connect('open-state-changed', function() {
|
||||||
|
statusMenu_updateHaveHibernate.call(statusMenu);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._previousUpdateSuspendOrPowerOff = statusMenu._updateSuspendOrPowerOff;
|
||||||
|
statusMenu._updateSuspendOrPowerOff = statusMenu_updateSuspendOrPowerOff;
|
||||||
|
|
||||||
|
this._settingsChangedId = this.settings.connect('changed', function() {
|
||||||
|
statusMenu._updateSuspendOrPowerOff();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
let statusMenu = Main.panel.statusArea.userMenu;
|
||||||
|
|
||||||
|
this.suspendItem.destroy();
|
||||||
|
this.hibernateItem.destroy();
|
||||||
|
this.powerOffItem.destroy();
|
||||||
|
|
||||||
|
statusMenu.menu.disconnect(this._openStateChangedId);
|
||||||
|
this.settings.disconnect(this._settingsChangedId);
|
||||||
|
|
||||||
|
statusMenu._updateSuspendOrPowerOff = this._previousUpdateSuspendOrPowerOff;
|
||||||
|
statusMenu._updateSuspendOrPowerOff();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Put your extension initialization code here
|
||||||
|
function init(metadata) {
|
||||||
|
return (extension = new Extension());
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extension-id": "@extension_id@",
|
||||||
|
"uuid": "@uuid@",
|
||||||
|
"settings-schema": "@gschemaname@",
|
||||||
|
"gettext-domain": "@gettext_domain@",
|
||||||
|
"name": "Alternative Status Menu",
|
||||||
|
"description": "Replaces GNOME Shell Status Menu with one showing Suspend/Hibernate and Power Off as separate items",
|
||||||
|
"shell-version": [ "@shell_current@" ],
|
||||||
|
"url": "@url@"
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<schemalist gettext-domain="gnome-shell-extensions">
|
||||||
|
<schema id="org.gnome.shell.extensions.alternative-status-menu" path="/org/gnome/shell/extensions/alternative-status-menu/">
|
||||||
|
<key name="allow-suspend" type="b">
|
||||||
|
<default>true</default>
|
||||||
|
<_summary>Enable suspending</_summary>
|
||||||
|
<_description>Control the visibility of the Suspend menu item</_description>
|
||||||
|
</key>
|
||||||
|
<key name="allow-hibernate" type="b">
|
||||||
|
<default>true</default>
|
||||||
|
<_summary>Enable hibernating</_summary>
|
||||||
|
<_description>Control the visibility of the Hibernate menu item</_description>
|
||||||
|
</key>
|
||||||
|
</schema>
|
||||||
|
</schemalist>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.popup-alternating-menu-item:alternate {
|
||||||
|
font-weight: normal !important;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
EXTENSION_ID = apps-menu
|
||||||
|
|
||||||
|
include ../../extension.mk
|
||||||
+368
-449
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
|||||||
extension_data += configure_file(
|
|
||||||
input: metadata_name + '.in',
|
|
||||||
output: metadata_name,
|
|
||||||
configuration: metadata_conf
|
|
||||||
)
|
|
||||||
extension_data += files('stylesheet.css')
|
|
||||||
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
{
|
{
|
||||||
"extension-id": "@extension_id@",
|
"extension-id": "@extension_id@",
|
||||||
"uuid": "@uuid@",
|
"uuid": "@uuid@",
|
||||||
"settings-schema": "@gschemaname@",
|
|
||||||
"gettext-domain": "@gettext_domain@",
|
"gettext-domain": "@gettext_domain@",
|
||||||
"name": "Applications Menu",
|
"name": "Applications Menu",
|
||||||
"description": "Add a category-based menu for applications.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME's GitLab instance instead.",
|
"description": "Add a gnome 2.x style menu for applications",
|
||||||
"original-authors": [ "e2002@bk.ru", "debarshir@gnome.org" ],
|
"original-authors": [ "e2002@bk.ru", "debarshir@gnome.org" ],
|
||||||
"shell-version": [ "@shell_current@" ],
|
"shell-version": [ "@shell_current@" ],
|
||||||
"url": "@url@"
|
"url": "@url@"
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
<schemalist gettext-domain="gnome-shell-extensions">
|
|
||||||
<schema id="org.gnome.shell.extensions.apps-menu"
|
|
||||||
path="/org/gnome/shell/extensions/apps-menu/">
|
|
||||||
<key name="apps-menu-toggle-menu" type="as">
|
|
||||||
<default>["<Alt>F1"]</default>
|
|
||||||
<summary>Keybinding to open the applications menu</summary>
|
|
||||||
<description>
|
|
||||||
Keybinding to open the applications menu.
|
|
||||||
</description>
|
|
||||||
</key>
|
|
||||||
</schema>
|
|
||||||
</schemalist>
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
EXTENSION_ID = auto-move-windows
|
||||||
|
|
||||||
|
EXTRA_MODULES = prefs.js
|
||||||
|
|
||||||
|
include ../../extension.mk
|
||||||
|
include ../../settings.mk
|
||||||
|
|
||||||
@@ -1,136 +1,167 @@
|
|||||||
// -*- 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
|
||||||
|
|
||||||
import Shell from 'gi://Shell';
|
const Glib = imports.gi.GLib;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
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 {
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
constructor(settings) {
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
|
const Convenience = Me.imports.convenience;
|
||||||
|
|
||||||
|
const SETTINGS_KEY = 'application-list';
|
||||||
|
|
||||||
|
let settings;
|
||||||
|
|
||||||
|
const WindowMover = new Lang.Class({
|
||||||
|
Name: 'AutoMoveWindows.WindowMover',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
this._settings = settings;
|
this._settings = settings;
|
||||||
this._appSystem = Shell.AppSystem.get_default();
|
this._windowTracker = Shell.WindowTracker.get_default();
|
||||||
this._appConfigs = new Map();
|
|
||||||
this._appData = new Map();
|
|
||||||
|
|
||||||
this._appSystem.connectObject('installed-changed',
|
let display = global.screen.get_display();
|
||||||
() => this._updateAppData(), this);
|
// Connect after so the handler from ShellWindowTracker has already run
|
||||||
|
this._windowCreatedId = display.connect_after('window-created', Lang.bind(this, this._findAndMove));
|
||||||
|
},
|
||||||
|
|
||||||
this._settings.connect('changed', this._updateAppConfigs.bind(this));
|
destroy: function() {
|
||||||
this._updateAppConfigs();
|
if (this._windowCreatedId) {
|
||||||
}
|
global.screen.get_display().disconnect(this._windowCreatedId);
|
||||||
|
this._windowCreatedId = 0;
|
||||||
_updateAppConfigs() {
|
|
||||||
this._appConfigs.clear();
|
|
||||||
|
|
||||||
this._settings.get_strv('application-list').forEach(v => {
|
|
||||||
let [appId, num] = v.split(':');
|
|
||||||
this._appConfigs.set(appId, parseInt(num) - 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
this._updateAppData();
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateAppData() {
|
|
||||||
let ids = [...this._appConfigs.keys()];
|
|
||||||
let removedApps = [...this._appData.keys()]
|
|
||||||
.filter(a => !ids.includes(a.id));
|
|
||||||
removedApps.forEach(app => {
|
|
||||||
app.disconnectObject(this);
|
|
||||||
this._appData.delete(app);
|
|
||||||
});
|
|
||||||
|
|
||||||
let addedApps = ids
|
|
||||||
.map(id => this._appSystem.lookup_app(id))
|
|
||||||
.filter(app => app && !this._appData.has(app));
|
|
||||||
addedApps.forEach(app => {
|
|
||||||
app.connectObject('window-changed',
|
|
||||||
this._appWindowsChanged.bind(this), this);
|
|
||||||
this._appData.set(app, {windows: app.get_windows()});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this._appSystem.disconnectObject(this);
|
|
||||||
|
|
||||||
if (this._settings) {
|
|
||||||
this._settings.run_dispose();
|
|
||||||
this._settings = null;
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
this._appConfigs.clear();
|
_ensureAtLeastWorkspaces: function(num, window) {
|
||||||
this._updateAppData();
|
for (let j = global.screen.n_workspaces; j <= num; j++) {
|
||||||
}
|
window.change_workspace_by_index(j-1, false, global.get_current_time());
|
||||||
|
global.screen.append_new_workspace(false, 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_moveWindow(window, workspaceNum) {
|
_findAndMove: function(display, window, noRecurse) {
|
||||||
if (window.skip_taskbar || window.is_on_all_workspaces())
|
if (!this._windowTracker.is_window_interesting(window))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// ensure we have the required number of workspaces
|
let spaces = this._settings.get_strv(SETTINGS_KEY);
|
||||||
let workspaceManager = global.workspace_manager;
|
|
||||||
for (let i = workspaceManager.n_workspaces; i <= workspaceNum; i++) {
|
let app = this._windowTracker.get_window_app(window);
|
||||||
window.change_workspace_by_index(i - 1, false);
|
if (!app) {
|
||||||
workspaceManager.append_new_workspace(false, 0);
|
if (!noRecurse) {
|
||||||
|
// window is not tracked yet
|
||||||
|
Mainloop.idle_add(Lang.bind(this, function() {
|
||||||
|
this._findAndMove(display, window, true);
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
} else
|
||||||
|
log ('Cannot find application for window');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
let app_id = app.get_id();
|
||||||
|
for ( let j = 0 ; j < spaces.length; j++ ) {
|
||||||
|
let apps_to_space = spaces[j].split(":");
|
||||||
|
// Match application id
|
||||||
|
if (apps_to_space[0] == app_id) {
|
||||||
|
let workspace_num = parseInt(apps_to_space[1]) - 1;
|
||||||
|
|
||||||
window.change_workspace_by_index(workspaceNum, false);
|
if (workspace_num >= global.screen.n_workspaces)
|
||||||
}
|
this._ensureAtLeastWorkspaces(workspace_num, window);
|
||||||
|
|
||||||
_appWindowsChanged(app) {
|
window.change_workspace_by_index(workspace_num, false, global.get_current_time());
|
||||||
let data = this._appData.get(app);
|
|
||||||
let windows = app.get_windows();
|
|
||||||
|
|
||||||
// If get_compositor_private() returns non-NULL on a removed windows,
|
|
||||||
// the window still exists and is just moved to a different workspace
|
|
||||||
// or something; assume it'll be added back immediately, so keep it
|
|
||||||
// to avoid moving it again
|
|
||||||
windows.push(...data.windows.filter(w => {
|
|
||||||
return !windows.includes(w) && w.get_compositor_private() !== null;
|
|
||||||
}));
|
|
||||||
|
|
||||||
let workspaceNum = this._appConfigs.get(app.id);
|
|
||||||
windows.filter(w => !data.windows.includes(w)).forEach(window => {
|
|
||||||
this._moveWindow(window, workspaceNum);
|
|
||||||
});
|
|
||||||
data.windows = windows;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class AutoMoveExtension extends Extension {
|
|
||||||
enable() {
|
|
||||||
this._prevCheckWorkspaces = Main.wm._workspaceTracker._checkWorkspaces;
|
|
||||||
Main.wm._workspaceTracker._checkWorkspaces =
|
|
||||||
this._getCheckWorkspaceOverride(this._prevCheckWorkspaces);
|
|
||||||
this._windowMover = new WindowMover(this.getSettings());
|
|
||||||
}
|
|
||||||
|
|
||||||
disable() {
|
|
||||||
Main.wm._workspaceTracker._checkWorkspaces = this._prevCheckWorkspaces;
|
|
||||||
this._windowMover.destroy();
|
|
||||||
delete this._windowMover;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getCheckWorkspaceOverride(originalMethod) {
|
|
||||||
/* eslint-disable no-invalid-this */
|
|
||||||
return function () {
|
|
||||||
const 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
|
|
||||||
keepAliveWorkspaces.forEach(ws => (ws._keepAliveId = 1));
|
|
||||||
originalMethod.call(this);
|
|
||||||
keepAliveWorkspaces.forEach(ws => delete ws._keepAliveId);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
/* eslint-enable no-invalid-this */
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let prevCheckWorkspaces;
|
||||||
|
let winMover;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
Convenience.initTranslations();
|
||||||
|
settings = Convenience.getSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
function myCheckWorkspaces() {
|
||||||
|
let i;
|
||||||
|
let emptyWorkspaces = new Array(Main._workspaces.length);
|
||||||
|
|
||||||
|
for (i = 0; i < Main._workspaces.length; i++) {
|
||||||
|
let lastRemoved = Main._workspaces[i]._lastRemovedWindow;
|
||||||
|
if (lastRemoved &&
|
||||||
|
(lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
|
||||||
|
lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
|
||||||
|
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG))
|
||||||
|
emptyWorkspaces[i] = false;
|
||||||
|
else
|
||||||
|
emptyWorkspaces[i] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let windows = global.get_window_actors();
|
||||||
|
for (i = 0; i < windows.length; i++) {
|
||||||
|
let win = windows[i];
|
||||||
|
|
||||||
|
if (win.get_meta_window().is_on_all_workspaces())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let workspaceIndex = win.get_workspace();
|
||||||
|
emptyWorkspaces[workspaceIndex] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have an empty workspace at the end, add one
|
||||||
|
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
|
||||||
|
global.screen.append_new_workspace(false, global.get_current_time());
|
||||||
|
emptyWorkspaces.push(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||||
|
let activeIsLast = activeWorkspaceIndex == global.screen.n_workspaces - 2;
|
||||||
|
let removingTrailWorkspaces = (emptyWorkspaces[activeWorkspaceIndex] &&
|
||||||
|
activeIsLast);
|
||||||
|
// Don't enter the overview when removing multiple empty workspaces at startup
|
||||||
|
let showOverview = (removingTrailWorkspaces &&
|
||||||
|
!emptyWorkspaces.every(function(x) { return x; }));
|
||||||
|
|
||||||
|
if (removingTrailWorkspaces) {
|
||||||
|
// "Merge" the empty workspace we are removing with the one at the end
|
||||||
|
Main.wm.blockAnimations();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete other empty workspaces; do it from the end to avoid index changes
|
||||||
|
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
|
||||||
|
if (emptyWorkspaces[i])
|
||||||
|
global.screen.remove_workspace(Main._workspaces[i], global.get_current_time());
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removingTrailWorkspaces) {
|
||||||
|
global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
|
||||||
|
|
||||||
|
Main.wm.unblockAnimations();
|
||||||
|
|
||||||
|
if (!Main.overview.visible && showOverview)
|
||||||
|
Main.overview.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
Main._checkWorkspacesId = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function enable() {
|
||||||
|
prevCheckWorkspaces = Main._checkWorkspaces;
|
||||||
|
if (Meta.prefs_get_dynamic_workspaces())
|
||||||
|
Main._checkWorkspaces = myCheckWorkspaces;
|
||||||
|
|
||||||
|
winMover = new WindowMover();
|
||||||
|
}
|
||||||
|
|
||||||
|
function disable() {
|
||||||
|
Main._checkWorkspaces = prevCheckWorkspaces;
|
||||||
|
winMover.destroy();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
extension_data += configure_file(
|
|
||||||
input: metadata_name + '.in',
|
|
||||||
output: metadata_name,
|
|
||||||
configuration: metadata_conf
|
|
||||||
)
|
|
||||||
|
|
||||||
extension_sources += files('prefs.js')
|
|
||||||
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"settings-schema": "@gschemaname@",
|
"settings-schema": "@gschemaname@",
|
||||||
"gettext-domain": "@gettext_domain@",
|
"gettext-domain": "@gettext_domain@",
|
||||||
"name": "Auto Move Windows",
|
"name": "Auto Move Windows",
|
||||||
"description": "Move applications to specific workspaces when they create windows.",
|
"description": "Move applications to specific workspaces when they create windows",
|
||||||
"shell-version": [ "@shell_current@" ],
|
"shell-version": [ "@shell_current@" ],
|
||||||
"original-authors": [ "alessandro.crismani@gmail.com", "thomas.bouffon@gmail.com" ],
|
"original-authors": [ "alessandro.crismani@gmail.com", "thomas.bouffon@gmail.com" ],
|
||||||
"url": "@url@"
|
"url": "@url@"
|
||||||
|
|||||||
+2
-2
@@ -3,8 +3,8 @@
|
|||||||
<key name="application-list" type="as">
|
<key name="application-list" type="as">
|
||||||
<!-- FIXME: should be a(su), when JS supports more of GVariant -->
|
<!-- FIXME: should be a(su), when JS supports more of GVariant -->
|
||||||
<default>[ ]</default>
|
<default>[ ]</default>
|
||||||
<summary>Application and workspace list</summary>
|
<_summary>Application and workspace list</_summary>
|
||||||
<description>A list of strings, each containing an application id (desktop file name), followed by a colon and the workspace number</description>
|
<_description>A list of strings, each containing an application id (desktop file name), followed by a colon and the workspace number</_description>
|
||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
</schemalist>
|
</schemalist>
|
||||||
@@ -1,348 +1,260 @@
|
|||||||
// -*- 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
|
||||||
|
|
||||||
import Adw from 'gi://Adw';
|
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||||
import Gio from 'gi://Gio';
|
const Gio = imports.gi.Gio;
|
||||||
import GLib from 'gi://GLib';
|
const GLib = imports.gi.GLib;
|
||||||
import GObject from 'gi://GObject';
|
const GObject = imports.gi.GObject;
|
||||||
import Gtk from 'gi://Gtk';
|
const GMenu = imports.gi.GMenu;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
|
||||||
import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
const N_ = function(e) { return e };
|
||||||
|
|
||||||
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
|
const Convenience = Me.imports.convenience;
|
||||||
|
|
||||||
const SETTINGS_KEY = 'application-list';
|
const SETTINGS_KEY = 'application-list';
|
||||||
|
|
||||||
const WORKSPACE_MAX = 36; // compiled in limit of mutter
|
const WORKSPACE_MAX = 36; // compiled in limit of mutter
|
||||||
|
|
||||||
class NewItem extends GObject.Object {}
|
const Columns = {
|
||||||
GObject.registerClass(NewItem);
|
APPINFO: 0,
|
||||||
|
DISPLAY_NAME: 1,
|
||||||
|
ICON: 2,
|
||||||
|
WORKSPACE: 3,
|
||||||
|
ADJUSTMENT: 4
|
||||||
|
};
|
||||||
|
|
||||||
class NewItemModel extends GObject.Object {
|
const Widget = new GObject.Class({
|
||||||
static [GObject.interfaces] = [Gio.ListModel];
|
Name: 'AutoMoveWindows.Prefs.Widget',
|
||||||
static {
|
GTypeName: 'AutoMoveWindowsPrefsWidget',
|
||||||
GObject.registerClass(this);
|
Extends: Gtk.Grid,
|
||||||
}
|
|
||||||
|
|
||||||
#item = new NewItem();
|
_init: function(params) {
|
||||||
|
this.parent(params);
|
||||||
|
this.set_orientation(Gtk.Orientation.VERTICAL);
|
||||||
|
|
||||||
vfunc_get_item_type() {
|
this._settings = Convenience.getSettings();
|
||||||
return NewItem;
|
this._settings.connect('changed', Lang.bind(this, this._refresh));
|
||||||
}
|
this._changedPermitted = false;
|
||||||
|
|
||||||
vfunc_get_n_items() {
|
this._store = new Gtk.ListStore();
|
||||||
return 1;
|
this._store.set_column_types([Gio.AppInfo, GObject.TYPE_STRING, Gio.Icon, GObject.TYPE_INT,
|
||||||
}
|
Gtk.Adjustment]);
|
||||||
|
|
||||||
vfunc_get_item(_pos) {
|
this._treeView = new Gtk.TreeView({ model: this._store,
|
||||||
return this.#item;
|
hexpand: true, vexpand: true });
|
||||||
}
|
this._treeView.get_selection().set_mode(Gtk.SelectionMode.SINGLE);
|
||||||
}
|
|
||||||
|
|
||||||
class Rule extends GObject.Object {
|
let appColumn = new Gtk.TreeViewColumn({ expand: true, sort_column_id: Columns.DISPLAY_NAME,
|
||||||
static [GObject.properties] = {
|
title: _("Application") });
|
||||||
'app-info': GObject.ParamSpec.object(
|
let iconRenderer = new Gtk.CellRendererPixbuf;
|
||||||
'app-info', 'app-info', 'app-info',
|
appColumn.pack_start(iconRenderer, false);
|
||||||
GObject.ParamFlags.READWRITE,
|
appColumn.add_attribute(iconRenderer, "gicon", Columns.ICON);
|
||||||
Gio.DesktopAppInfo),
|
let nameRenderer = new Gtk.CellRendererText;
|
||||||
'workspace': GObject.ParamSpec.uint(
|
appColumn.pack_start(nameRenderer, true);
|
||||||
'workspace', 'workspace', 'workspace',
|
appColumn.add_attribute(nameRenderer, "text", Columns.DISPLAY_NAME);
|
||||||
GObject.ParamFlags.READWRITE,
|
this._treeView.append_column(appColumn);
|
||||||
1, WORKSPACE_MAX, 1),
|
|
||||||
};
|
|
||||||
|
|
||||||
static {
|
let workspaceColumn = new Gtk.TreeViewColumn({ title: _("Workspace"),
|
||||||
GObject.registerClass(this);
|
sort_column_id: Columns.WORKSPACE });
|
||||||
}
|
let workspaceRenderer = new Gtk.CellRendererSpin({ editable: true });
|
||||||
}
|
workspaceRenderer.connect('edited', Lang.bind(this, this._workspaceEdited));
|
||||||
|
workspaceColumn.pack_start(workspaceRenderer, true);
|
||||||
|
workspaceColumn.add_attribute(workspaceRenderer, "adjustment", Columns.ADJUSTMENT);
|
||||||
|
workspaceColumn.add_attribute(workspaceRenderer, "text", Columns.WORKSPACE);
|
||||||
|
this._treeView.append_column(workspaceColumn);
|
||||||
|
|
||||||
class RulesList extends GObject.Object {
|
this.add(this._treeView);
|
||||||
static [GObject.interfaces] = [Gio.ListModel];
|
|
||||||
static {
|
|
||||||
GObject.registerClass(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
#settings;
|
let toolbar = new Gtk.Toolbar();
|
||||||
#rules = [];
|
toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR);
|
||||||
#changedId;
|
this.add(toolbar);
|
||||||
|
|
||||||
constructor(settings) {
|
let newButton = new Gtk.ToolButton({ stock_id: Gtk.STOCK_NEW,
|
||||||
super();
|
label: _("Add rule"),
|
||||||
|
is_important: true });
|
||||||
|
newButton.connect('clicked', Lang.bind(this, this._createNew));
|
||||||
|
toolbar.add(newButton);
|
||||||
|
|
||||||
this.#settings = settings;
|
let delButton = new Gtk.ToolButton({ stock_id: Gtk.STOCK_DELETE });
|
||||||
this.#changedId =
|
delButton.connect('clicked', Lang.bind(this, this._deleteSelected));
|
||||||
this.#settings.connect(`changed::${SETTINGS_KEY}`,
|
toolbar.add(delButton);
|
||||||
() => this.#sync());
|
|
||||||
this.#sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
append(appInfo) {
|
this._changedPermitted = true;
|
||||||
const pos = this.#rules.length;
|
this._refresh();
|
||||||
|
},
|
||||||
|
|
||||||
this.#rules.push(new Rule({appInfo}));
|
_createNew: function() {
|
||||||
this.#saveRules();
|
let dialog = new Gtk.Dialog({ title: _("Create new matching rule"),
|
||||||
|
transient_for: this.get_toplevel(),
|
||||||
|
modal: true });
|
||||||
|
dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL);
|
||||||
|
dialog.add_button(_("Add"), Gtk.ResponseType.OK);
|
||||||
|
dialog.set_default_response(Gtk.ResponseType.OK);
|
||||||
|
|
||||||
this.items_changed(pos, 0, 1);
|
let grid = new Gtk.Grid({ column_spacing: 10,
|
||||||
}
|
row_spacing: 15,
|
||||||
|
margin: 10 });
|
||||||
|
dialog._appChooser = new Gtk.AppChooserWidget({ show_all: true });
|
||||||
|
grid.attach(dialog._appChooser, 0, 0, 2, 1);
|
||||||
|
grid.attach(new Gtk.Label({ label: _("Workspace") }),
|
||||||
|
0, 1, 1, 1);
|
||||||
|
let adjustment = new Gtk.Adjustment({ lower: 1,
|
||||||
|
upper: WORKSPACE_MAX,
|
||||||
|
step_increment: 1
|
||||||
|
});
|
||||||
|
dialog._spin = new Gtk.SpinButton({ adjustment: adjustment,
|
||||||
|
snap_to_ticks: true });
|
||||||
|
dialog._spin.set_value(1);
|
||||||
|
grid.attach(dialog._spin, 1, 1, 1, 1);
|
||||||
|
dialog.get_content_area().add(grid);
|
||||||
|
|
||||||
remove(id) {
|
dialog.connect('response', Lang.bind(this, function(dialog, id) {
|
||||||
const pos = this.#rules.findIndex(r => r.appInfo.get_id() === id);
|
if (id != Gtk.ResponseType.OK) {
|
||||||
if (pos < 0)
|
dialog.destroy();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.#rules.splice(pos, 1);
|
let appInfo = dialog._appChooser.get_app_info();
|
||||||
this.#saveRules();
|
if (!appInfo)
|
||||||
|
return;
|
||||||
|
let index = Math.floor(dialog._spin.value);
|
||||||
|
if (isNaN(index) || index < 0)
|
||||||
|
index = 1;
|
||||||
|
|
||||||
this.items_changed(pos, 1, 0);
|
this._changedPermitted = false;
|
||||||
}
|
if (!this._appendItem(appInfo.get_id(), index)) {
|
||||||
|
this._changedPermitted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let iter = this._store.append();
|
||||||
|
let adj = new Gtk.Adjustment({ lower: 1,
|
||||||
|
upper: WORKSPACE_MAX,
|
||||||
|
step_increment: 1,
|
||||||
|
value: index });
|
||||||
|
this._store.set(iter,
|
||||||
|
[Columns.APPINFO, Columns.ICON, Columns.DISPLAY_NAME, Columns.WORKSPACE, Columns.ADJUSTMENT],
|
||||||
|
[appInfo, appInfo.get_icon(), appInfo.get_display_name(), index, adj]);
|
||||||
|
this._changedPermitted = true;
|
||||||
|
|
||||||
changeWorkspace(id, workspace) {
|
|
||||||
const pos = this.#rules.findIndex(r => r.appInfo.get_id() === id);
|
|
||||||
if (pos < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.#rules[pos].set({workspace});
|
|
||||||
this.#saveRules();
|
|
||||||
}
|
|
||||||
|
|
||||||
#saveRules() {
|
|
||||||
this.#settings.block_signal_handler(this.#changedId);
|
|
||||||
this.#settings.set_strv(SETTINGS_KEY,
|
|
||||||
this.#rules.map(r => `${r.app_info.get_id()}:${r.workspace}`));
|
|
||||||
this.#settings.unblock_signal_handler(this.#changedId);
|
|
||||||
}
|
|
||||||
|
|
||||||
#sync() {
|
|
||||||
const removed = this.#rules.length;
|
|
||||||
|
|
||||||
this.#rules = [];
|
|
||||||
for (const stringRule of this.#settings.get_strv(SETTINGS_KEY)) {
|
|
||||||
const [id, workspace] = stringRule.split(':');
|
|
||||||
const appInfo = Gio.DesktopAppInfo.new(id);
|
|
||||||
if (appInfo)
|
|
||||||
this.#rules.push(new Rule({appInfo, workspace}));
|
|
||||||
else
|
|
||||||
log(`Invalid ID ${id}`);
|
|
||||||
}
|
|
||||||
this.items_changed(0, removed, this.#rules.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
vfunc_get_item_type() {
|
|
||||||
return Rule;
|
|
||||||
}
|
|
||||||
|
|
||||||
vfunc_get_n_items() {
|
|
||||||
return this.#rules.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
vfunc_get_item(pos) {
|
|
||||||
return this.#rules[pos] ?? null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AutoMoveSettingsWidget extends Adw.PreferencesGroup {
|
|
||||||
static {
|
|
||||||
GObject.registerClass(this);
|
|
||||||
|
|
||||||
this.install_action('rules.add', null, self => self._addNewRule());
|
|
||||||
this.install_action('rules.remove', 's',
|
|
||||||
(self, name, param) => self._rules.remove(param.unpack()));
|
|
||||||
this.install_action('rules.change-workspace', '(si)',
|
|
||||||
(self, name, param) => self._rules.changeWorkspace(...param.deepUnpack()));
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(settings) {
|
|
||||||
super({
|
|
||||||
title: _('Workspace Rules'),
|
|
||||||
});
|
|
||||||
|
|
||||||
this._settings = settings;
|
|
||||||
this._rules = new RulesList(this._settings);
|
|
||||||
|
|
||||||
const store = new Gio.ListStore({item_type: Gio.ListModel});
|
|
||||||
const listModel = new Gtk.FlattenListModel({model: store});
|
|
||||||
store.append(this._rules);
|
|
||||||
store.append(new NewItemModel());
|
|
||||||
|
|
||||||
this._list = new Gtk.ListBox({
|
|
||||||
selection_mode: Gtk.SelectionMode.NONE,
|
|
||||||
css_classes: ['boxed-list'],
|
|
||||||
});
|
|
||||||
this.add(this._list);
|
|
||||||
|
|
||||||
this._list.bind_model(listModel, item => {
|
|
||||||
return item instanceof NewItem
|
|
||||||
? new NewRuleRow()
|
|
||||||
: new RuleRow(item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_addNewRule() {
|
|
||||||
const dialog = new NewRuleDialog(this.get_root(), this._settings);
|
|
||||||
dialog.connect('response', (dlg, id) => {
|
|
||||||
const appInfo = id === Gtk.ResponseType.OK
|
|
||||||
? dialog.get_widget().get_app_info() : null;
|
|
||||||
if (appInfo)
|
|
||||||
this._rules.append(appInfo);
|
|
||||||
dialog.destroy();
|
dialog.destroy();
|
||||||
});
|
}));
|
||||||
dialog.show();
|
dialog.show_all();
|
||||||
|
},
|
||||||
|
|
||||||
|
_deleteSelected: function() {
|
||||||
|
let [any, model, iter] = this._treeView.get_selection().get_selected();
|
||||||
|
|
||||||
|
if (any) {
|
||||||
|
let appInfo = this._store.get_value(iter, Columns.APPINFO);
|
||||||
|
|
||||||
|
this._changedPermitted = false;
|
||||||
|
this._removeItem(appInfo.get_id());
|
||||||
|
this._store.remove(iter);
|
||||||
|
this._changedPermitted = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_workspaceEdited: function(renderer, pathString, text) {
|
||||||
|
let index = parseInt(text);
|
||||||
|
if (isNaN(index) || index < 0)
|
||||||
|
index = 1;
|
||||||
|
let path = Gtk.TreePath.new_from_string(pathString);
|
||||||
|
let [model, iter] = this._store.get_iter(path);
|
||||||
|
let appInfo = this._store.get_value(iter, Columns.APPINFO);
|
||||||
|
|
||||||
|
this._changedPermitted = false;
|
||||||
|
this._changeItem(appInfo.get_id(), index);
|
||||||
|
this._store.set_value(iter, Columns.WORKSPACE, index);
|
||||||
|
this._changedPermitted = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_refresh: function() {
|
||||||
|
if (!this._changedPermitted)
|
||||||
|
// Ignore this notification, model is being modified outside
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._store.clear();
|
||||||
|
|
||||||
|
let currentItems = this._settings.get_strv(SETTINGS_KEY);
|
||||||
|
let validItems = [ ];
|
||||||
|
for (let i = 0; i < currentItems.length; i++) {
|
||||||
|
let [id, index] = currentItems[i].split(':');
|
||||||
|
let appInfo = Gio.DesktopAppInfo.new(id);
|
||||||
|
if (!appInfo)
|
||||||
|
continue;
|
||||||
|
validItems.push(currentItems[i]);
|
||||||
|
|
||||||
|
let iter = this._store.append();
|
||||||
|
let adj = new Gtk.Adjustment({ lower: 1,
|
||||||
|
upper: WORKSPACE_MAX,
|
||||||
|
step_increment: 1,
|
||||||
|
value: index });
|
||||||
|
this._store.set(iter,
|
||||||
|
[Columns.APPINFO, Columns.ICON, Columns.DISPLAY_NAME, Columns.WORKSPACE, Columns.ADJUSTMENT],
|
||||||
|
[appInfo, appInfo.get_icon(), appInfo.get_display_name(), parseInt(index), adj]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validItems.length != currentItems.length) // some items were filtered out
|
||||||
|
this._settings.set_strv(SETTINGS_KEY, validItems);
|
||||||
|
},
|
||||||
|
|
||||||
|
_appendItem: function(id, workspace) {
|
||||||
|
let currentItems = this._settings.get_strv(SETTINGS_KEY);
|
||||||
|
let alreadyHave = currentItems.map(function(el) {
|
||||||
|
return el.split(':')[0];
|
||||||
|
}).indexOf(id) != -1;
|
||||||
|
|
||||||
|
if (alreadyHave) {
|
||||||
|
printerr("Already have an item for this id");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentItems.push(id + ':' + workspace);
|
||||||
|
this._settings.set_strv(SETTINGS_KEY, currentItems);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeItem: function(id) {
|
||||||
|
let currentItems = this._settings.get_strv(SETTINGS_KEY);
|
||||||
|
let index = currentItems.map(function(el) {
|
||||||
|
return el.split(':')[0];
|
||||||
|
}).indexOf(id);
|
||||||
|
|
||||||
|
if (index < 0)
|
||||||
|
return;
|
||||||
|
currentItems.splice(index, 1);
|
||||||
|
this._settings.set_strv(SETTINGS_KEY, currentItems);
|
||||||
|
},
|
||||||
|
|
||||||
|
_changeItem: function(id, workspace) {
|
||||||
|
let currentItems = this._settings.get_strv(SETTINGS_KEY);
|
||||||
|
let index = currentItems.map(function(el) {
|
||||||
|
return el.split(':')[0];
|
||||||
|
}).indexOf(id);
|
||||||
|
|
||||||
|
if (index < 0)
|
||||||
|
currentItems.push(id + ':' + workspace);
|
||||||
|
else
|
||||||
|
currentItems[index] = id + ':' + workspace;
|
||||||
|
this._settings.set_strv(SETTINGS_KEY, currentItems);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
Convenience.initTranslations();
|
||||||
}
|
}
|
||||||
|
|
||||||
class WorkspaceSelector extends Gtk.Widget {
|
function buildPrefsWidget() {
|
||||||
static [GObject.properties] = {
|
let widget = new Widget();
|
||||||
'number': GObject.ParamSpec.uint(
|
widget.show_all();
|
||||||
'number', 'number', 'number',
|
|
||||||
GObject.ParamFlags.READWRITE,
|
|
||||||
1, WORKSPACE_MAX, 1),
|
|
||||||
};
|
|
||||||
|
|
||||||
static {
|
return widget;
|
||||||
GObject.registerClass(this);
|
|
||||||
|
|
||||||
this.set_layout_manager_type(Gtk.BoxLayout);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.layout_manager.spacing = 6;
|
|
||||||
|
|
||||||
const label = new Gtk.Label({
|
|
||||||
xalign: 1,
|
|
||||||
margin_end: 6,
|
|
||||||
});
|
|
||||||
this.bind_property('number',
|
|
||||||
label, 'label',
|
|
||||||
GObject.BindingFlags.SYNC_CREATE);
|
|
||||||
label.set_parent(this);
|
|
||||||
|
|
||||||
const buttonProps = {
|
|
||||||
css_classes: ['circular'],
|
|
||||||
valign: Gtk.Align.CENTER,
|
|
||||||
};
|
|
||||||
|
|
||||||
this._decButton = new Gtk.Button({
|
|
||||||
icon_name: 'list-remove-symbolic',
|
|
||||||
...buttonProps,
|
|
||||||
});
|
|
||||||
this._decButton.set_parent(this);
|
|
||||||
this._decButton.connect('clicked', () => this.number--);
|
|
||||||
|
|
||||||
this._incButton = new Gtk.Button({
|
|
||||||
icon_name: 'list-add-symbolic',
|
|
||||||
...buttonProps,
|
|
||||||
});
|
|
||||||
this._incButton.set_parent(this);
|
|
||||||
this._incButton.connect('clicked', () => this.number++);
|
|
||||||
|
|
||||||
this.connect('notify::number', () => this._syncButtons());
|
|
||||||
this._syncButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
_syncButtons() {
|
|
||||||
this._decButton.sensitive = this.number > 1;
|
|
||||||
this._incButton.sensitive = this.number < WORKSPACE_MAX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RuleRow extends Adw.ActionRow {
|
|
||||||
static {
|
|
||||||
GObject.registerClass(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(rule) {
|
|
||||||
const {appInfo} = rule;
|
|
||||||
const id = appInfo.get_id();
|
|
||||||
|
|
||||||
super({
|
|
||||||
activatable: false,
|
|
||||||
title: rule.appInfo.get_display_name(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const icon = new Gtk.Image({
|
|
||||||
css_classes: ['icon-dropshadow'],
|
|
||||||
gicon: appInfo.get_icon(),
|
|
||||||
pixel_size: 32,
|
|
||||||
});
|
|
||||||
this.add_prefix(icon);
|
|
||||||
|
|
||||||
const wsButton = new WorkspaceSelector();
|
|
||||||
rule.bind_property('workspace',
|
|
||||||
wsButton, 'number',
|
|
||||||
GObject.BindingFlags.SYNC_CREATE);
|
|
||||||
this.add_suffix(wsButton);
|
|
||||||
|
|
||||||
wsButton.connect('notify::number', () => {
|
|
||||||
this.activate_action('rules.change-workspace',
|
|
||||||
new GLib.Variant('(si)', [id, wsButton.number]));
|
|
||||||
});
|
|
||||||
|
|
||||||
const button = new Gtk.Button({
|
|
||||||
action_name: 'rules.remove',
|
|
||||||
action_target: new GLib.Variant('s', id),
|
|
||||||
icon_name: 'edit-delete-symbolic',
|
|
||||||
has_frame: false,
|
|
||||||
valign: Gtk.Align.CENTER,
|
|
||||||
});
|
|
||||||
this.add_suffix(button);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NewRuleRow extends Gtk.ListBoxRow {
|
|
||||||
static {
|
|
||||||
GObject.registerClass(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super({
|
|
||||||
action_name: 'rules.add',
|
|
||||||
child: new Gtk.Image({
|
|
||||||
icon_name: 'list-add-symbolic',
|
|
||||||
pixel_size: 16,
|
|
||||||
margin_top: 12,
|
|
||||||
margin_bottom: 12,
|
|
||||||
margin_start: 12,
|
|
||||||
margin_end: 12,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
this.update_property(
|
|
||||||
[Gtk.AccessibleProperty.LABEL], [_('Add Rule')]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NewRuleDialog extends Gtk.AppChooserDialog {
|
|
||||||
static {
|
|
||||||
GObject.registerClass(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(parent, settings) {
|
|
||||||
super({
|
|
||||||
transient_for: parent,
|
|
||||||
modal: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
this._settings = settings;
|
|
||||||
|
|
||||||
this.get_widget().set({
|
|
||||||
show_all: true,
|
|
||||||
show_other: true, // hide more button
|
|
||||||
});
|
|
||||||
|
|
||||||
this.get_widget().connect('application-selected',
|
|
||||||
this._updateSensitivity.bind(this));
|
|
||||||
this._updateSensitivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateSensitivity() {
|
|
||||||
const rules = this._settings.get_strv(SETTINGS_KEY);
|
|
||||||
const appInfo = this.get_widget().get_app_info();
|
|
||||||
this.set_response_sensitive(Gtk.ResponseType.OK,
|
|
||||||
appInfo && !rules.some(i => i.startsWith(appInfo.get_id())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class AutoMovePrefs extends ExtensionPreferences {
|
|
||||||
getPreferencesWidget() {
|
|
||||||
return new AutoMoveSettingsWidget(this.getSettings());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
/* This extensions requires no special styling */
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
EXTENSION_ID = drive-menu
|
||||||
|
|
||||||
|
include ../../extension.mk
|
||||||
+155
-177
@@ -1,208 +1,186 @@
|
|||||||
// Drive menu extension
|
// Drive menu extension
|
||||||
import Clutter from 'gi://Clutter';
|
const Gio = imports.gi.Gio;
|
||||||
import Gio from 'gi://Gio';
|
const Lang = imports.lang;
|
||||||
import GObject from 'gi://GObject';
|
const St = imports.gi.St;
|
||||||
import Shell from 'gi://Shell';
|
const Shell = imports.gi.Shell;
|
||||||
import St from 'gi://St';
|
|
||||||
|
|
||||||
import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
|
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
|
||||||
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 Panel = imports.ui.panel;
|
||||||
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
import * as ShellMountOperation from 'resource:///org/gnome/shell/ui/shellMountOperation.js';
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||||
|
|
||||||
Gio._promisify(Gio.File.prototype, 'query_filesystem_info_async');
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
|
const Convenience = Me.imports.convenience;
|
||||||
|
|
||||||
class MountMenuItem extends PopupMenu.PopupBaseMenuItem {
|
const MountMenuItem = new Lang.Class({
|
||||||
static {
|
Name: 'DriveMenu.MountMenuItem',
|
||||||
GObject.registerClass(this);
|
Extends: PopupMenu.PopupBaseMenuItem,
|
||||||
|
|
||||||
|
_init: function(mount) {
|
||||||
|
this.parent();
|
||||||
|
|
||||||
|
this.label = new St.Label({ text: mount.get_name() });
|
||||||
|
this.addActor(this.label);
|
||||||
|
this.actor.label_actor = this.label;
|
||||||
|
|
||||||
|
this.mount = mount;
|
||||||
|
|
||||||
|
let ejectIcon = new St.Icon({ icon_name: 'media-eject-symbolic',
|
||||||
|
style_class: 'popup-menu-icon ' });
|
||||||
|
let ejectButton = new St.Button({ child: ejectIcon });
|
||||||
|
ejectButton.connect('clicked', Lang.bind(this, this._eject));
|
||||||
|
this.addActor(ejectButton);
|
||||||
|
},
|
||||||
|
|
||||||
|
_eject: function() {
|
||||||
|
let mountOp = new ShellMountOperation.ShellMountOperation(this.mount);
|
||||||
|
|
||||||
|
if (this.mount.can_eject())
|
||||||
|
this.mount.eject_with_operation(Gio.MountUnmountFlags.NONE,
|
||||||
|
mountOp.mountOp,
|
||||||
|
null, // Gio.Cancellable
|
||||||
|
Lang.bind(this, this._ejectFinish));
|
||||||
|
else
|
||||||
|
this.mount.unmount_with_operation(Gio.MountUnmountFlags.NONE,
|
||||||
|
mountOp.mountOp,
|
||||||
|
null, // Gio.Cancellable
|
||||||
|
Lang.bind(this, this._unmountFinish));
|
||||||
|
},
|
||||||
|
|
||||||
|
_unmountFinish: function(mount, result) {
|
||||||
|
try {
|
||||||
|
mount.unmount_with_operation_finish(result);
|
||||||
|
} catch(e) {
|
||||||
|
this._reportFailure(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_ejectFinish: function(mount, result) {
|
||||||
|
try {
|
||||||
|
mount.eject_with_operation_finish(result);
|
||||||
|
} catch(e) {
|
||||||
|
this._reportFailure(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_reportFailure: function(exception) {
|
||||||
|
let msg = _("Ejecting drive '%s' failed:").format(this.mount.get_name());
|
||||||
|
Main.notifyError(msg, exception.message);
|
||||||
|
},
|
||||||
|
|
||||||
|
activate: function(event) {
|
||||||
|
Gio.AppInfo.launch_default_for_uri(this.mount.get_root().get_uri(),
|
||||||
|
global.create_app_launch_context());
|
||||||
|
|
||||||
|
this.parent(event);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
constructor(mount) {
|
const DriveMenu = new Lang.Class({
|
||||||
super({
|
Name: 'DriveMenu.DriveMenu',
|
||||||
style_class: 'drive-menu-item',
|
Extends: PanelMenu.SystemStatusButton,
|
||||||
});
|
|
||||||
|
|
||||||
this.label = new St.Label({
|
_init: function() {
|
||||||
text: mount.get_name(),
|
this.parent('media-eject-symbolic', _("Removable devices"));
|
||||||
x_expand: true,
|
|
||||||
y_align: Clutter.ActorAlign.CENTER,
|
|
||||||
});
|
|
||||||
this.add_child(this.label);
|
|
||||||
this.label_actor = this.label;
|
|
||||||
|
|
||||||
this.mount = mount;
|
this._monitor = Gio.VolumeMonitor.get();
|
||||||
|
this._addedId = this._monitor.connect('mount-added', Lang.bind(this, function(monitor, mount) {
|
||||||
|
this._addMount(mount);
|
||||||
|
this._updateMenuVisibility();
|
||||||
|
}));
|
||||||
|
this._removedId = this._monitor.connect('mount-removed', Lang.bind(this, function(monitor, mount) {
|
||||||
|
this._removeMount(mount);
|
||||||
|
this._updateMenuVisibility();
|
||||||
|
}));
|
||||||
|
|
||||||
this.connect('destroy', this._onDestroy.bind(this));
|
this._mounts = [ ];
|
||||||
|
|
||||||
let ejectIcon = new St.Icon({
|
this._monitor.get_mounts().forEach(Lang.bind(this, this._addMount));
|
||||||
icon_name: 'media-eject-symbolic',
|
|
||||||
style_class: 'popup-menu-icon',
|
|
||||||
});
|
|
||||||
let ejectButton = new St.Button({
|
|
||||||
child: ejectIcon,
|
|
||||||
style_class: 'button',
|
|
||||||
});
|
|
||||||
ejectButton.connect('clicked', this._eject.bind(this));
|
|
||||||
this.add(ejectButton);
|
|
||||||
|
|
||||||
this.hide();
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
|
this.menu.addAction(_("Open File"), function(event) {
|
||||||
|
let appSystem = Shell.AppSystem.get_default();
|
||||||
|
let app = appSystem.lookup_app('nautilus.desktop');
|
||||||
|
app.activate_full(-1, event.get_time());
|
||||||
|
});
|
||||||
|
|
||||||
mount.connectObject('changed',
|
this._updateMenuVisibility();
|
||||||
() => this._syncVisibility(), this);
|
},
|
||||||
this._syncVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
async _isInteresting() {
|
_updateMenuVisibility: function() {
|
||||||
if (!this.mount.can_eject() && !this.mount.can_unmount())
|
if (this._mounts.length > 0)
|
||||||
return false;
|
this.actor.show();
|
||||||
if (this.mount.is_shadowed())
|
else
|
||||||
|
this.actor.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_isMountInteresting: function(mount) {
|
||||||
|
if (!mount.can_eject() && !mount.can_unmount())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
let volume = this.mount.get_volume();
|
let volume = mount.get_volume();
|
||||||
|
|
||||||
if (volume)
|
if (volume == null) {
|
||||||
return volume.get_identifier('class') !== 'network';
|
// probably a GDaemonMount, could be network or
|
||||||
|
// local, but we can't tell; assume it's local for now
|
||||||
const root = this.mount.get_root();
|
return true;
|
||||||
|
|
||||||
try {
|
|
||||||
const attr = Gio.FILE_ATTRIBUTE_FILESYSTEM_REMOTE;
|
|
||||||
const info = await root.query_filesystem_info_async(attr, null);
|
|
||||||
return !info.get_attribute_boolean(attr);
|
|
||||||
} catch (e) {
|
|
||||||
log(`Failed to query filesystem: ${e.message}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hack, fall back to looking at GType
|
return volume.get_identifier('class') != 'network';
|
||||||
return Gio._LocalFilePrototype.isPrototypeOf(root);
|
},
|
||||||
}
|
|
||||||
|
|
||||||
async _syncVisibility() {
|
_addMount: function(mount) {
|
||||||
this.visible = await this._isInteresting();
|
if (!this._isMountInteresting(mount))
|
||||||
}
|
return;
|
||||||
|
|
||||||
_eject() {
|
let item = new MountMenuItem(mount);
|
||||||
let unmountArgs = [
|
this._mounts.unshift(item);
|
||||||
Gio.MountUnmountFlags.NONE,
|
this.menu.addMenuItem(item, 0);
|
||||||
new ShellMountOperation.ShellMountOperation(this.mount).mountOp,
|
},
|
||||||
null, // Gio.Cancellable
|
|
||||||
];
|
|
||||||
|
|
||||||
if (this.mount.can_eject()) {
|
_removeMount: function(mount) {
|
||||||
this.mount.eject_with_operation(...unmountArgs,
|
if (!this._isMountInteresting(mount))
|
||||||
this._ejectFinish.bind(this));
|
return;
|
||||||
} else {
|
|
||||||
this.mount.unmount_with_operation(...unmountArgs,
|
for (let i = 0; i < this._mounts.length; i++) {
|
||||||
this._unmountFinish.bind(this));
|
let item = this._mounts[i];
|
||||||
|
if (item.mount == mount) {
|
||||||
|
item.destroy();
|
||||||
|
this._mounts.splice(i, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log ('Removing a mount that was never added to the menu');
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
if (this._connectedId) {
|
||||||
|
this._monitor.disconnect(this._connectedId);
|
||||||
|
this._monitor.disconnect(this._disconnectedId);
|
||||||
|
this._connectedId = 0;
|
||||||
|
this._disconnectedId = 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
_unmountFinish(mount, result) {
|
this.parent();
|
||||||
try {
|
},
|
||||||
mount.unmount_with_operation_finish(result);
|
});
|
||||||
} catch (e) {
|
|
||||||
this._reportFailure(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ejectFinish(mount, result) {
|
function init() {
|
||||||
try {
|
Convenience.initTranslations();
|
||||||
mount.eject_with_operation_finish(result);
|
|
||||||
} catch (e) {
|
|
||||||
this._reportFailure(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_reportFailure(exception) {
|
|
||||||
// TRANSLATORS: %s is the filesystem name
|
|
||||||
let msg = _('Ejecting drive “%s” failed:').format(this.mount.get_name());
|
|
||||||
Main.notifyError(msg, exception.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
activate(event) {
|
|
||||||
let uri = this.mount.get_root().get_uri();
|
|
||||||
let context = global.create_app_launch_context(event.get_time(), -1);
|
|
||||||
Gio.AppInfo.launch_default_for_uri(uri, context);
|
|
||||||
|
|
||||||
super.activate(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class DriveMenu extends PanelMenu.Button {
|
let _indicator;
|
||||||
static {
|
|
||||||
GObject.registerClass(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
function enable() {
|
||||||
super(0.5, _('Removable devices'));
|
_indicator = new DriveMenu;
|
||||||
|
Main.panel.addToStatusArea('drive-menu', _indicator);
|
||||||
let icon = new St.Icon({
|
|
||||||
icon_name: 'media-eject-symbolic',
|
|
||||||
style_class: 'system-status-icon',
|
|
||||||
});
|
|
||||||
|
|
||||||
this.add_child(icon);
|
|
||||||
|
|
||||||
this._monitor = Gio.VolumeMonitor.get();
|
|
||||||
this._monitor.connectObject(
|
|
||||||
'mount-added', (monitor, mount) => this._addMount(mount),
|
|
||||||
'mount-removed', (monitor, mount) => {
|
|
||||||
this._removeMount(mount);
|
|
||||||
this._updateMenuVisibility();
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
this._mounts = [];
|
|
||||||
|
|
||||||
this._monitor.get_mounts().forEach(this._addMount.bind(this));
|
|
||||||
|
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
||||||
this.menu.addAction(_('Open Files'), event => {
|
|
||||||
let appSystem = Shell.AppSystem.get_default();
|
|
||||||
let app = appSystem.lookup_app('org.gnome.Nautilus.desktop');
|
|
||||||
app.activate_full(-1, event.get_time());
|
|
||||||
});
|
|
||||||
|
|
||||||
this._updateMenuVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateMenuVisibility() {
|
|
||||||
if (this._mounts.filter(i => i.visible).length > 0)
|
|
||||||
this.show();
|
|
||||||
else
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
_addMount(mount) {
|
|
||||||
let item = new MountMenuItem(mount);
|
|
||||||
this._mounts.unshift(item);
|
|
||||||
this.menu.addMenuItem(item, 0);
|
|
||||||
|
|
||||||
item.connect('notify::visible', () => this._updateMenuVisibility());
|
|
||||||
}
|
|
||||||
|
|
||||||
_removeMount(mount) {
|
|
||||||
for (let i = 0; i < this._mounts.length; i++) {
|
|
||||||
let item = this._mounts[i];
|
|
||||||
if (item.mount === mount) {
|
|
||||||
item.destroy();
|
|
||||||
this._mounts.splice(i, 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log('Removing a mount that was never added to the menu');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class PlaceMenuExtension extends Extension {
|
function disable() {
|
||||||
enable() {
|
_indicator.destroy();
|
||||||
this._indicator = new DriveMenu();
|
|
||||||
Main.panel.addToStatusArea('drive-menu', this._indicator);
|
|
||||||
}
|
|
||||||
|
|
||||||
disable() {
|
|
||||||
this._indicator.destroy();
|
|
||||||
delete this._indicator;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
extension_data += configure_file(
|
|
||||||
input: metadata_name + '.in',
|
|
||||||
output: metadata_name,
|
|
||||||
configuration: metadata_conf
|
|
||||||
)
|
|
||||||
extension_data += files('stylesheet.css')
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"settings-schema": "@gschemaname@",
|
"settings-schema": "@gschemaname@",
|
||||||
"gettext-domain": "@gettext_domain@",
|
"gettext-domain": "@gettext_domain@",
|
||||||
"name": "Removable Drive Menu",
|
"name": "Removable Drive Menu",
|
||||||
"description": "A status menu for accessing and unmounting removable devices.",
|
"description": "A status menu for accessing and unmounting removable devices",
|
||||||
"shell-version": [ "@shell_current@" ],
|
"shell-version": [ "@shell_current@" ],
|
||||||
"url": "@url@"
|
"url": "@url@"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1 @@
|
|||||||
.drive-menu-item { spacing: 12px; }
|
/* This extensions requires no custom styling */
|
||||||
|
|
||||||
.drive-menu-item .button {
|
|
||||||
border-radius: 99px;
|
|
||||||
padding: 3px;
|
|
||||||
min-height: auto;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
EXTENSION_ID = example
|
||||||
|
|
||||||
|
EXTRA_MODULES = prefs.js
|
||||||
|
|
||||||
|
include ../../extension.mk
|
||||||
|
include ../../settings.mk
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
|
||||||
|
// Sample extension code, makes clicking on the panel show a message
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
|
||||||
|
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
|
const Convenience = Me.imports.convenience;
|
||||||
|
|
||||||
|
function _showHello() {
|
||||||
|
let settings = Convenience.getSettings();
|
||||||
|
let text = settings.get_string('hello-text') || _("Hello, world!");
|
||||||
|
|
||||||
|
let label = new St.Label({ style_class: 'helloworld-label', text: text });
|
||||||
|
let monitor = Main.layoutManager.primaryMonitor;
|
||||||
|
global.stage.add_actor(label);
|
||||||
|
label.set_position(Math.floor (monitor.width / 2 - label.width / 2), Math.floor(monitor.height / 2 - label.height / 2));
|
||||||
|
Mainloop.timeout_add(3000, function () { label.destroy(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put your extension initialization code here
|
||||||
|
function init(metadata) {
|
||||||
|
log ('Example extension initalized');
|
||||||
|
|
||||||
|
Convenience.initTranslations();
|
||||||
|
}
|
||||||
|
|
||||||
|
let signalId;
|
||||||
|
|
||||||
|
function enable() {
|
||||||
|
log ('Example extension enabled');
|
||||||
|
|
||||||
|
Main.panel.actor.reactive = true;
|
||||||
|
signalId = Main.panel.actor.connect('button-release-event', _showHello);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disable() {
|
||||||
|
log ('Example extension disabled');
|
||||||
|
|
||||||
|
if (signalId) {
|
||||||
|
Main.panel.actor.disconnect(signalId);
|
||||||
|
signalId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extension-id": "@extension_id@",
|
||||||
|
"uuid": "@uuid@",
|
||||||
|
"settings-schema": "@gschemaname@",
|
||||||
|
"gettext-domain": "@gettext_domain@",
|
||||||
|
"name": "Hello, World!",
|
||||||
|
"description": "An example extension to show how it works. Shows Hello, world when clicking on the top panel.",
|
||||||
|
"shell-version": [ "@shell_current@" ],
|
||||||
|
"url": "@url@"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<schemalist gettext-domain="gnome-shell-extensions">
|
||||||
|
<schema id="org.gnome.shell.extensions.example" path="/org/gnome/shell/extensions/example/">
|
||||||
|
<key name="hello-text" type="s">
|
||||||
|
<default>''</default>
|
||||||
|
<_summary>Alternative greeting text.</_summary>
|
||||||
|
<_description>If not empty, it contains the text that will be shown when clicking on the panel.</_description>
|
||||||
|
</key>
|
||||||
|
</schema>
|
||||||
|
</schemalist>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
|
||||||
|
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const GObject = imports.gi.GObject;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
|
|
||||||
|
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
|
||||||
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
|
const Convenience = Me.imports.convenience;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
Convenience.initTranslations();
|
||||||
|
}
|
||||||
|
|
||||||
|
const ExamplePrefsWidget = new GObject.Class({
|
||||||
|
Name: 'Example.Prefs.Widget',
|
||||||
|
GTypeName: 'ExamplePrefsWidget',
|
||||||
|
Extends: Gtk.Grid,
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
this.parent(params);
|
||||||
|
this.margin = this.row_spacing = this.column_spacing = 10;
|
||||||
|
|
||||||
|
// TRANSLATORS: Example is the name of the extension, should not be
|
||||||
|
// translated
|
||||||
|
let primaryText = _("Example aims to show how to build well behaved \
|
||||||
|
extensions for the Shell and as such it has little functionality on its own.\n\
|
||||||
|
Nevertheless it's possible to customize the greeting message.");
|
||||||
|
|
||||||
|
this.attach(new Gtk.Label({ label: primaryText, wrap: true }), 0, 0, 2, 1);
|
||||||
|
|
||||||
|
this.attach(new Gtk.Label({ label: '<b>' + _("Message:") + '</b>', use_markup: true }),
|
||||||
|
0, 1, 1, 1);
|
||||||
|
|
||||||
|
let entry = new Gtk.Entry({ hexpand: true });
|
||||||
|
this.attach(entry, 1, 1, 1, 1);
|
||||||
|
|
||||||
|
this._settings = Convenience.getSettings();
|
||||||
|
this._settings.bind('hello-text', entry, 'text', Gio.SettingsBindFlags.DEFAULT);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function buildPrefsWidget() {
|
||||||
|
let widget = new ExamplePrefsWidget();
|
||||||
|
widget.show_all();
|
||||||
|
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
.screenshot-sizer-message {
|
/* Example stylesheet */
|
||||||
|
.helloworld-label {
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
background-color: rgba(10,10,10,0.7);
|
background-color: rgba(10,10,10,0.7);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: .5em;
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
EXTENSION_ID = launch-new-instance
|
||||||
|
|
||||||
|
include ../../extension.mk
|
||||||
@@ -1,22 +1,37 @@
|
|||||||
import {AppIcon} from 'resource:///org/gnome/shell/ui/appDisplay.js';
|
const Main = imports.ui.main;
|
||||||
import {InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js';
|
const AppDisplay = imports.ui.appDisplay;
|
||||||
|
|
||||||
export default class Extension {
|
var _onActivateOriginal = null;
|
||||||
constructor() {
|
var _activateResultOriginal = null;
|
||||||
this._injectionManager = new InjectionManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
enable() {
|
function _onActivate(event) {
|
||||||
this._injectionManager.overrideMethod(AppIcon.prototype, 'activate',
|
|
||||||
originalMethod => {
|
|
||||||
return function () {
|
|
||||||
// eslint-disable-next-line no-invalid-this
|
|
||||||
originalMethod.call(this, 2);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
disable() {
|
this.emit('launching');
|
||||||
this._injectionManager.clear();
|
|
||||||
}
|
if (this._onActivateOverride) {
|
||||||
|
this._onActivateOverride(event);
|
||||||
|
} else {
|
||||||
|
this.app.open_new_window(-1);
|
||||||
|
}
|
||||||
|
Main.overview.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _activateResult(app) {
|
||||||
|
app.open_new_window(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
function enable() {
|
||||||
|
_onActivateOriginal = AppDisplay.AppIcon.prototype._onActivate;
|
||||||
|
AppDisplay.AppIcon.prototype._onActivate = _onActivate;
|
||||||
|
|
||||||
|
_activateResultOriginal = AppDisplay.AppSearchProvider.prototype.activateResult;
|
||||||
|
AppDisplay.AppSearchProvider.prototype.activateResult = _activateResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
function disable() {
|
||||||
|
AppDisplay.AppIcon.prototype._onActivate = _onActivateOriginal;
|
||||||
|
AppDisplay.AppSearchProvider.prototype.activateResult = _activateResultOriginal;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
extension_data += configure_file(
|
|
||||||
input: metadata_name + '.in',
|
|
||||||
output: metadata_name,
|
|
||||||
configuration: metadata_conf
|
|
||||||
)
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"settings-schema": "@gschemaname@",
|
"settings-schema": "@gschemaname@",
|
||||||
"gettext-domain": "@gettext_domain@",
|
"gettext-domain": "@gettext_domain@",
|
||||||
"name": "Launch new instance",
|
"name": "Launch new instance",
|
||||||
"description": "Always launch a new instance when clicking in the dash or the application view.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME's GitLab instance instead.",
|
"description": "Always launch a new instance",
|
||||||
"shell-version": [ "@shell_current@" ],
|
"shell-version": [ "@shell_current@" ],
|
||||||
"url": "@url@"
|
"url": "@url@"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
/* This extensions requires no special styling */
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
extension_data += configure_file(
|
|
||||||
input: metadata_name + '.in',
|
|
||||||
output: metadata_name,
|
|
||||||
configuration: metadata_conf
|
|
||||||
)
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
extension_schemas = []
|
|
||||||
js_sources = []
|
|
||||||
|
|
||||||
metadata_name = 'metadata.json'
|
|
||||||
|
|
||||||
foreach e : enabled_extensions
|
|
||||||
uuid = e + uuid_suffix
|
|
||||||
|
|
||||||
metadata_conf = configuration_data()
|
|
||||||
metadata_conf.set('extension_id', e)
|
|
||||||
metadata_conf.set('uuid', uuid)
|
|
||||||
metadata_conf.set('gschemaname', 'org.gnome.shell.extensions.' + e)
|
|
||||||
metadata_conf.set('gettext_domain', gettext_domain)
|
|
||||||
metadata_conf.set('shell_current', shell_version)
|
|
||||||
metadata_conf.set('url', 'https://gitlab.gnome.org/GNOME/gnome-shell-extensions')
|
|
||||||
|
|
||||||
extension_sources = files(e + '/extension.js')
|
|
||||||
extension_data = []
|
|
||||||
|
|
||||||
subdir(e)
|
|
||||||
|
|
||||||
install_data (extension_sources + extension_data,
|
|
||||||
install_dir: join_paths(extensiondir, uuid)
|
|
||||||
)
|
|
||||||
endforeach
|
|
||||||
|
|
||||||
install_data (extension_schemas,
|
|
||||||
install_dir: schemadir
|
|
||||||
)
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
extension_data += configure_file(
|
|
||||||
input: metadata_name + '.in',
|
|
||||||
output: metadata_name,
|
|
||||||
configuration: metadata_conf
|
|
||||||
)
|
|
||||||
|
|
||||||
# extension_data += files('stylesheet.css')
|
|
||||||
# extension_sources += files('prefs.js')
|
|
||||||
# extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
EXTENSION_ID = native-window-placement
|
||||||
|
|
||||||
|
include ../../extension.mk
|
||||||
|
include ../../settings.mk
|
||||||
|
|
||||||
@@ -1,139 +1,227 @@
|
|||||||
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
|
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
|
||||||
import Clutter from 'gi://Clutter';
|
// import just everything from workspace.js:
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const Pango = imports.gi.Pango;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
import {Extension, InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js';
|
const DND = imports.ui.dnd;
|
||||||
|
const Lightbox = imports.ui.lightbox;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Overview = imports.ui.overview;
|
||||||
|
const Panel = imports.ui.panel;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
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';
|
const WindowPositionFlags = Workspace.WindowPositionFlags;
|
||||||
import * as Workspace from 'resource:///org/gnome/shell/ui/workspace.js';
|
|
||||||
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
|
const Convenience = Me.imports.convenience;
|
||||||
|
|
||||||
// testing settings for natural window placement strategy:
|
// testing settings for natural window placement strategy:
|
||||||
|
const WINDOW_PLACEMENT_NATURAL_FILLGAPS = true; // enlarge windows at the end to fill gaps // not implemented yet
|
||||||
|
const WINDOW_PLACEMENT_NATURAL_GRID_FALLBACK = true; // fallback to grid mode if all windows have the same size and positions. // not implemented yet
|
||||||
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)
|
||||||
const WINDOW_PLACEMENT_NATURAL_GAPS = 5; // half of the minimum gap between windows
|
const WINDOW_PLACEMENT_NATURAL_GAPS = 5; // half of the minimum gap between windows
|
||||||
const WINDOW_PLACEMENT_NATURAL_MAX_TRANSLATIONS = 5000; // safety limit for preventing endless loop if something is wrong in the algorithm
|
const WINDOW_PLACEMENT_NATURAL_MAX_TRANSLATIONS = 5000; // safety limit for preventing endless loop if something is wrong in the algorithm
|
||||||
|
|
||||||
class Rect {
|
const PLACE_WINDOW_CAPTIONS_ON_TOP = true; // place window titles in overview on top of windows with overlap parameter
|
||||||
constructor(x, y, width, height) {
|
|
||||||
|
const WORKSPACE_BORDER_GAP = 10; // minimum gap between the workspace area and the workspace selector
|
||||||
|
const WINDOW_AREA_TOP_GAP = 20; // minimum gap between the workspace area and the top border. This keeps window captions and close buttons visible. 13px (26/2) should currently be enough.
|
||||||
|
|
||||||
|
const BUTTON_LAYOUT_SCHEMA = 'org.gnome.shell.overrides';
|
||||||
|
const BUTTON_LAYOUT_KEY = 'button-layout';
|
||||||
|
|
||||||
|
function injectToFunction(parent, name, func) {
|
||||||
|
let origin = parent[name];
|
||||||
|
parent[name] = function() {
|
||||||
|
let ret;
|
||||||
|
ret = origin.apply(this, arguments);
|
||||||
|
if (ret === undefined)
|
||||||
|
ret = func.apply(this, arguments);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Rect = new Lang.Class({
|
||||||
|
Name: 'NativeWindowPlacement.Rect',
|
||||||
|
|
||||||
|
_init: function(x, y, width, height) {
|
||||||
[this.x, this.y, this.width, this.height] = [x, y, width, height];
|
[this.x, this.y, this.width, this.height] = [x, y, width, height];
|
||||||
}
|
},
|
||||||
|
|
||||||
// used in _calculateWindowTransformationsNatural to replace Meta.Rectangle that is too slow.
|
/**
|
||||||
copy() {
|
* used in _calculateWindowTransformationsNatural to replace Meta.Rectangle that is too slow.
|
||||||
|
*/
|
||||||
|
copy: function() {
|
||||||
return new Rect(this.x, this.y, this.width, this.height);
|
return new Rect(this.x, this.y, this.width, this.height);
|
||||||
}
|
},
|
||||||
|
|
||||||
union(rect2) {
|
union: function(rect2) {
|
||||||
let dest = this.copy();
|
let dest = this.copy();
|
||||||
if (rect2.x < dest.x) {
|
if (rect2.x < dest.x)
|
||||||
|
{
|
||||||
dest.width += dest.x - rect2.x;
|
dest.width += dest.x - rect2.x;
|
||||||
dest.x = rect2.x;
|
dest.x = rect2.x;
|
||||||
}
|
}
|
||||||
if (rect2.y < dest.y) {
|
if (rect2.y < dest.y)
|
||||||
|
{
|
||||||
dest.height += dest.y - rect2.y;
|
dest.height += dest.y - rect2.y;
|
||||||
dest.y = rect2.y;
|
dest.y = rect2.y;
|
||||||
}
|
}
|
||||||
if (rect2.x + rect2.width > dest.x + dest.width)
|
if (rect2.x + rect2.width > dest.x + dest.width)
|
||||||
dest.width = rect2.x + rect2.width - dest.x;
|
dest.width = rect2.x + rect2.width - dest.x;
|
||||||
if (rect2.y + rect2.height > dest.y + dest.height)
|
if (rect2.y + rect2.height > dest.y + dest.height)
|
||||||
dest.height = rect2.y + rect2.height - dest.y;
|
dest.height = rect2.y + rect2.height - dest.y;
|
||||||
|
|
||||||
return dest;
|
return dest;
|
||||||
}
|
},
|
||||||
|
|
||||||
adjusted(dx, dy, dx2, dy2) {
|
adjusted: function(dx, dy, dx2, dy2) {
|
||||||
let dest = this.copy();
|
let dest = this.copy();
|
||||||
dest.x += dx;
|
dest.x += dx;
|
||||||
dest.y += dy;
|
dest.y += dy;
|
||||||
dest.width += -dx + dx2;
|
dest.width += -dx + dx2;
|
||||||
dest.height += -dy + dy2;
|
dest.height += -dy + dy2;
|
||||||
return dest;
|
return dest;
|
||||||
}
|
},
|
||||||
|
|
||||||
overlap(rect2) {
|
overlap: function(rect2) {
|
||||||
return !(this.x + this.width <= rect2.x ||
|
return !((this.x + this.width <= rect2.x) ||
|
||||||
rect2.x + rect2.width <= this.x ||
|
(rect2.x + rect2.width <= this.x) ||
|
||||||
this.y + this.height <= rect2.y ||
|
(this.y + this.height <= rect2.y) ||
|
||||||
rect2.y + rect2.height <= this.y);
|
(rect2.y + rect2.height <= this.y));
|
||||||
}
|
},
|
||||||
|
|
||||||
center() {
|
center: function() {
|
||||||
return [this.x + this.width / 2, this.y + this.height / 2];
|
return [this.x + this.width / 2, this.y + this.height / 2];
|
||||||
}
|
},
|
||||||
|
|
||||||
translate(dx, dy) {
|
translate: function(dx, dy) {
|
||||||
this.x += dx;
|
this.x += dx;
|
||||||
this.y += dy;
|
this.y += dy;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let winInjections, workspaceInjections, connectedSignals;
|
||||||
|
|
||||||
|
function resetState() {
|
||||||
|
winInjections = { };
|
||||||
|
workspaceInjections = { };
|
||||||
|
connectedSignals = [ ];
|
||||||
}
|
}
|
||||||
|
|
||||||
class NaturalLayoutStrategy extends Workspace.LayoutStrategy {
|
function enable() {
|
||||||
constructor(params, settings) {
|
resetState();
|
||||||
super(params);
|
|
||||||
this._settings = settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
computeLayout(windows, _params) {
|
let settings = Convenience.getSettings();
|
||||||
return {
|
let useMoreScreen = settings.get_boolean('use-more-screen');
|
||||||
windows,
|
let windowCaptionsOnTop = settings.get_boolean('window-captions-on-top');
|
||||||
};
|
let signalId = settings.connect('changed::use-more-screen', function() {
|
||||||
}
|
useMoreScreen = settings.get_boolean('use-more-screen');
|
||||||
|
});
|
||||||
|
connectedSignals.push({ obj: settings, id: signalId });
|
||||||
|
|
||||||
/*
|
/**
|
||||||
|
* _calculateWindowTransformationsNatural:
|
||||||
|
* @clones: Array of #MetaWindow
|
||||||
|
*
|
||||||
* Returns clones with matching target coordinates and scales to arrange windows in a natural way that no overlap exists and relative window size is preserved.
|
* Returns clones with matching target coordinates and scales to arrange windows in a natural way that no overlap exists and relative window size is preserved.
|
||||||
* This function is almost a 1:1 copy of the function
|
* This function is almost a 1:1 copy of the function
|
||||||
* PresentWindowsEffect::calculateWindowTransformationsNatural() from KDE, see:
|
* PresentWindowsEffect::calculateWindowTransformationsNatural() from KDE, see:
|
||||||
* https://projects.kde.org/projects/kde/kdebase/kde-workspace/repository/revisions/master/entry/kwin/effects/presentwindows/presentwindows.cpp
|
* https://projects.kde.org/projects/kde/kdebase/kde-workspace/repository/revisions/master/entry/kwin/effects/presentwindows/presentwindows.cpp
|
||||||
*/
|
*/
|
||||||
computeWindowSlots(layout, area) {
|
Workspace.Workspace.prototype._calculateWindowTransformationsNatural = function(clones) {
|
||||||
// As we are using pseudo-random movement (See "slot") we need to make sure the list
|
// As we are using pseudo-random movement (See "slot") we need to make sure the list
|
||||||
// is always sorted the same way no matter which window is currently active.
|
// is always sorted the same way no matter which window is currently active.
|
||||||
|
|
||||||
let areaRect = new Rect(area.x, area.y, area.width, area.height);
|
let node = this.actor.get_theme_node();
|
||||||
let bounds = areaRect.copy();
|
let columnSpacing = node.get_length('-horizontal-spacing');
|
||||||
let clones = layout.windows;
|
let rowSpacing = node.get_length('-vertical-spacing');
|
||||||
|
let padding = {
|
||||||
|
left: node.get_padding(St.Side.LEFT),
|
||||||
|
top: node.get_padding(St.Side.TOP),
|
||||||
|
bottom: node.get_padding(St.Side.BOTTOM),
|
||||||
|
right: node.get_padding(St.Side.RIGHT),
|
||||||
|
};
|
||||||
|
|
||||||
|
let closeButtonHeight, captionHeight;
|
||||||
|
let leftBorder, rightBorder;
|
||||||
|
// If the window captions are below the window, put an additional gap to account for them
|
||||||
|
if (!windowCaptionsOnTop && this._windowOverlays.length) {
|
||||||
|
// All of the overlays have the same chrome sizes,
|
||||||
|
// so just pick the first one.
|
||||||
|
let overlay = this._windowOverlays[0];
|
||||||
|
[closeButtonHeight, captionHeight] = overlay.chromeHeights();
|
||||||
|
[leftBorder, rightBorder] = overlay.chromeWidths();
|
||||||
|
} else {
|
||||||
|
[closeButtonHeight, captionHeight] = [0, 0];
|
||||||
|
[leftBorder, rightBorder] = [0, 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
rowSpacing += captionHeight;
|
||||||
|
columnSpacing += (rightBorder + leftBorder) / 2;
|
||||||
|
padding.top += closeButtonHeight;
|
||||||
|
padding.bottom += captionHeight;
|
||||||
|
padding.left += leftBorder;
|
||||||
|
padding.right += rightBorder;
|
||||||
|
|
||||||
|
let area = new Rect(this._x + padding.left,
|
||||||
|
this._y + padding.top,
|
||||||
|
this._width - padding.left - padding.right,
|
||||||
|
this._height - padding.top - padding.bottom);
|
||||||
|
|
||||||
|
let bounds = area.copy();
|
||||||
|
|
||||||
let direction = 0;
|
let direction = 0;
|
||||||
let directions = [];
|
let directions = [];
|
||||||
let rects = [];
|
let rects = [];
|
||||||
for (let i = 0; i < clones.length; i++) {
|
for (let i = 0; i < clones.length; i++) {
|
||||||
// save rectangles into 4-dimensional arrays representing two corners of the rectangular: [left_x, top_y, right_x, bottom_y]
|
// save rectangles into 4-dimensional arrays representing two corners of the rectangular: [left_x, top_y, right_x, bottom_y]
|
||||||
let rect = clones[i].boundingBox;
|
let rect = clones[i].metaWindow.get_outer_rect();
|
||||||
rects[i] = new Rect(rect.x, rect.y, rect.width, rect.height);
|
rects[i] = new Rect(rect.x, rect.y, rect.width, rect.height);
|
||||||
bounds = bounds.union(rects[i]);
|
bounds = bounds.union(rects[i]);
|
||||||
|
|
||||||
// This is used when the window is on the edge of the screen to try to use as much screen real estate as possible.
|
// This is used when the window is on the edge of the screen to try to use as much screen real estate as possible.
|
||||||
directions[i] = direction;
|
directions[i] = direction;
|
||||||
direction++;
|
direction++;
|
||||||
if (direction === 4)
|
if (direction == 4) {
|
||||||
direction = 0;
|
direction = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let loopCounter = 0;
|
let loop_counter = 0;
|
||||||
let overlap;
|
let overlap;
|
||||||
do {
|
do {
|
||||||
overlap = false;
|
overlap = false;
|
||||||
for (let i = 0; i < rects.length; i++) {
|
for (let i = 0; i < rects.length; i++) {
|
||||||
for (let j = 0; j < rects.length; j++) {
|
for (let j = 0; j < rects.length; j++) {
|
||||||
let adjustments = [-1, -1, 1, 1]
|
if (i != j && rects[i].adjusted(-WINDOW_PLACEMENT_NATURAL_GAPS, -WINDOW_PLACEMENT_NATURAL_GAPS,
|
||||||
.map(v => (v *= WINDOW_PLACEMENT_NATURAL_GAPS));
|
WINDOW_PLACEMENT_NATURAL_GAPS, WINDOW_PLACEMENT_NATURAL_GAPS).overlap(
|
||||||
let iAdjusted = rects[i].adjusted(...adjustments);
|
rects[j].adjusted(-WINDOW_PLACEMENT_NATURAL_GAPS, -WINDOW_PLACEMENT_NATURAL_GAPS,
|
||||||
let jAdjusted = rects[j].adjusted(...adjustments);
|
WINDOW_PLACEMENT_NATURAL_GAPS, WINDOW_PLACEMENT_NATURAL_GAPS))) {
|
||||||
if (i !== j && iAdjusted.overlap(jAdjusted)) {
|
loop_counter++;
|
||||||
loopCounter++;
|
|
||||||
overlap = true;
|
overlap = true;
|
||||||
|
|
||||||
// TODO: something like a Point2D would be nicer here:
|
// TODO: something like a Point2D would be nicer here:
|
||||||
|
|
||||||
// Determine pushing direction
|
// Determine pushing direction
|
||||||
let iCenter = rects[i].center();
|
let i_center = rects[i].center();
|
||||||
let jCenter = rects[j].center();
|
let j_center = rects[j].center();
|
||||||
let diff = [jCenter[0] - iCenter[0], jCenter[1] - iCenter[1]];
|
let diff = [j_center[0] - i_center[0], j_center[1] - i_center[1]];
|
||||||
|
|
||||||
// Prevent dividing by zero and non-movement
|
// Prevent dividing by zero and non-movement
|
||||||
if (diff[0] === 0 && diff[1] === 0)
|
if (diff[0] == 0 && diff[1] == 0)
|
||||||
diff[0] = 1;
|
diff[0] = 1;
|
||||||
// Try to keep screen/workspace aspect ratio
|
// Try to keep screen/workspace aspect ratio
|
||||||
if (bounds.height / bounds.width > areaRect.height / areaRect.width)
|
if ( bounds.height / bounds.width > area.height / area.width )
|
||||||
diff[0] *= 2;
|
diff[0] *= 2;
|
||||||
else
|
else
|
||||||
diff[1] *= 2;
|
diff[1] *= 2;
|
||||||
@@ -148,7 +236,7 @@ class NaturalLayoutStrategy extends Workspace.LayoutStrategy {
|
|||||||
rects[j].translate(diff[0], diff[1]);
|
rects[j].translate(diff[0], diff[1]);
|
||||||
|
|
||||||
|
|
||||||
if (this._settings.get_boolean('use-more-screen')) {
|
if (useMoreScreen) {
|
||||||
// Try to keep the bounding rect the same aspect as the screen so that more
|
// Try to keep the bounding rect the same aspect as the screen so that more
|
||||||
// screen real estate is utilised. We do this by splitting the screen into nine
|
// screen real estate is utilised. We do this by splitting the screen into nine
|
||||||
// equal sections, if the window center is in any of the corner sections pull the
|
// equal sections, if the window center is in any of the corner sections pull the
|
||||||
@@ -162,33 +250,33 @@ class NaturalLayoutStrategy extends Workspace.LayoutStrategy {
|
|||||||
let xSection = Math.round((rects[i].x - bounds.x) / (bounds.width / 3));
|
let xSection = Math.round((rects[i].x - bounds.x) / (bounds.width / 3));
|
||||||
let ySection = Math.round((rects[i].y - bounds.y) / (bounds.height / 3));
|
let ySection = Math.round((rects[i].y - bounds.y) / (bounds.height / 3));
|
||||||
|
|
||||||
iCenter = rects[i].center();
|
let i_center = rects[i].center();
|
||||||
diff[0] = 0;
|
diff[0] = 0;
|
||||||
diff[1] = 0;
|
diff[1] = 0;
|
||||||
if (xSection !== 1 || ySection !== 1) { // Remove this if you want the center to pull as well
|
if (xSection != 1 || ySection != 1) { // Remove this if you want the center to pull as well
|
||||||
if (xSection === 1)
|
if (xSection == 1)
|
||||||
xSection = directions[i] / 2 ? 2 : 0;
|
xSection = (directions[i] / 2 ? 2 : 0);
|
||||||
if (ySection === 1)
|
if (ySection == 1)
|
||||||
ySection = directions[i] % 2 ? 2 : 0;
|
ySection = (directions[i] % 2 ? 2 : 0);
|
||||||
}
|
}
|
||||||
if (xSection === 0 && ySection === 0) {
|
if (xSection == 0 && ySection == 0) {
|
||||||
diff[0] = bounds.x - iCenter[0];
|
diff[0] = bounds.x - i_center[0];
|
||||||
diff[1] = bounds.y - iCenter[1];
|
diff[1] = bounds.y - i_center[1];
|
||||||
}
|
}
|
||||||
if (xSection === 2 && ySection === 0) {
|
if (xSection == 2 && ySection == 0) {
|
||||||
diff[0] = bounds.x + bounds.width - iCenter[0];
|
diff[0] = bounds.x + bounds.width - i_center[0];
|
||||||
diff[1] = bounds.y - iCenter[1];
|
diff[1] = bounds.y - i_center[1];
|
||||||
}
|
}
|
||||||
if (xSection === 2 && ySection === 2) {
|
if (xSection == 2 && ySection == 2) {
|
||||||
diff[0] = bounds.x + bounds.width - iCenter[0];
|
diff[0] = bounds.x + bounds.width - i_center[0];
|
||||||
diff[1] = bounds.y + bounds.height - iCenter[1];
|
diff[1] = bounds.y + bounds.height - i_center[1];
|
||||||
}
|
}
|
||||||
if (xSection === 0 && ySection === 2) {
|
if (xSection == 0 && ySection == 2) {
|
||||||
diff[0] = bounds.x - iCenter[0];
|
diff[0] = bounds.x - i_center[0];
|
||||||
diff[1] = bounds.y + bounds.height - iCenter[1];
|
diff[1] = bounds.y + bounds.height - i_center[1];
|
||||||
}
|
}
|
||||||
if (diff[0] !== 0 || diff[1] !== 0) {
|
if (diff[0] != 0 || diff[1] != 0) {
|
||||||
length = Math.sqrt(diff[0] * diff[0] + diff[1] * diff[1]);
|
let length = Math.sqrt(diff[0]*diff[0] + diff[1]*diff[1]);
|
||||||
diff[0] *= WINDOW_PLACEMENT_NATURAL_ACCURACY / length / 2; // /2 to make it less influencing than the normal center-move above
|
diff[0] *= WINDOW_PLACEMENT_NATURAL_ACCURACY / length / 2; // /2 to make it less influencing than the normal center-move above
|
||||||
diff[1] *= WINDOW_PLACEMENT_NATURAL_ACCURACY / length / 2;
|
diff[1] *= WINDOW_PLACEMENT_NATURAL_ACCURACY / length / 2;
|
||||||
rects[i].translate(diff[0], diff[1]);
|
rects[i].translate(diff[0], diff[1]);
|
||||||
@@ -201,99 +289,146 @@ class NaturalLayoutStrategy extends Workspace.LayoutStrategy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (overlap && loopCounter < WINDOW_PLACEMENT_NATURAL_MAX_TRANSLATIONS);
|
} while (overlap && loop_counter < WINDOW_PLACEMENT_NATURAL_MAX_TRANSLATIONS);
|
||||||
|
|
||||||
// Work out scaling by getting the most top-left and most bottom-right window coords.
|
// Work out scaling by getting the most top-left and most bottom-right window coords.
|
||||||
let scale;
|
let scale;
|
||||||
scale = Math.min(
|
scale = Math.min(area.width / bounds.width,
|
||||||
areaRect.width / bounds.width,
|
area.height / bounds.height,
|
||||||
areaRect.height / bounds.height,
|
1.0);
|
||||||
1.0);
|
|
||||||
|
|
||||||
// Make bounding rect fill the screen size for later steps
|
// Make bounding rect fill the screen size for later steps
|
||||||
bounds.x -= (areaRect.width - bounds.width * scale) / 2;
|
bounds.x = bounds.x - (area.width - bounds.width * scale) / 2;
|
||||||
bounds.y -= (areaRect.height - bounds.height * scale) / 2;
|
bounds.y = bounds.y - (area.height - bounds.height * scale) / 2;
|
||||||
bounds.width = areaRect.width / scale;
|
bounds.width = area.width / scale;
|
||||||
bounds.height = areaRect.height / scale;
|
bounds.height = area.height / scale;
|
||||||
|
|
||||||
// Move all windows back onto the screen and set their scale
|
// Move all windows back onto the screen and set their scale
|
||||||
for (let i = 0; i < rects.length; i++)
|
for (let i = 0; i < rects.length; i++) {
|
||||||
rects[i].translate(-bounds.x, -bounds.y);
|
rects[i].translate(-bounds.x, -bounds.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement the KDE part "Try to fill the gaps by enlarging windows if they have the space" here. (If this is wanted)
|
||||||
|
|
||||||
// rescale to workspace
|
// rescale to workspace
|
||||||
|
let scales = [];
|
||||||
|
|
||||||
|
let buttonOuterHeight, captionHeight;
|
||||||
|
let buttonOuterWidth = 0;
|
||||||
|
|
||||||
let slots = [];
|
let slots = [];
|
||||||
for (let i = 0; i < rects.length; i++) {
|
for (let i = 0; i < rects.length; i++) {
|
||||||
rects[i].x = rects[i].x * scale + areaRect.x;
|
rects[i].x = rects[i].x * scale + area.x;
|
||||||
rects[i].y = rects[i].y * scale + areaRect.y;
|
rects[i].y = rects[i].y * scale + area.y;
|
||||||
rects[i].width *= scale;
|
|
||||||
rects[i].height *= scale;
|
|
||||||
|
|
||||||
slots.push([rects[i].x, rects[i].y, rects[i].width, rects[i].height, clones[i]]);
|
slots.push([rects[i].x, rects[i].y, scale, clones[i]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return slots;
|
return slots;
|
||||||
}
|
}
|
||||||
}
|
workspaceInjections['_calculateWindowTransformationsNatural'] = undefined;
|
||||||
|
|
||||||
export default class NativeWindowPlacementExtension extends Extension {
|
/// map gnome shell's computeAllWindowSlots() to our window placement function
|
||||||
constructor(metadata) {
|
workspaceInjections['_computeAllWindowSlots'] = Workspace.Workspace.prototype._computeAllWindowSlots;
|
||||||
super(metadata);
|
Workspace.Workspace.prototype._computeAllWindowSlots = function(windows) {
|
||||||
|
return this._calculateWindowTransformationsNatural(windows);
|
||||||
this._injectionManager = new InjectionManager();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enable() {
|
/// position window titles on top of windows in overlay ////
|
||||||
const settings = this.getSettings();
|
if (windowCaptionsOnTop) {
|
||||||
|
winInjections['chromeHeights'] = Workspace.WindowOverlay.prototype.chromeHeights;
|
||||||
|
Workspace.WindowOverlay.prototype.chromeHeights = function () {
|
||||||
|
return [Math.max( this.closeButton.height - this.closeButton._overlap, this.title.height - this.title._overlap),
|
||||||
|
0];
|
||||||
|
};
|
||||||
|
|
||||||
const layoutProto = Workspace.WorkspaceLayout.prototype;
|
winInjections['relayout'] = Workspace.WindowOverlay.prototype.relayout;
|
||||||
const previewProto = WindowPreview.prototype;
|
Workspace.WindowOverlay.prototype.relayout = function(animate) {
|
||||||
|
let button = this.closeButton;
|
||||||
|
let title = this.title;
|
||||||
|
let border = this.border;
|
||||||
|
|
||||||
this._injectionManager.overrideMethod(layoutProto, '_createBestLayout', () => {
|
Tweener.removeTweens(button);
|
||||||
/* eslint-disable no-invalid-this */
|
Tweener.removeTweens(title);
|
||||||
return function () {
|
Tweener.removeTweens(border);
|
||||||
this._layoutStrategy = new NaturalLayoutStrategy({
|
|
||||||
monitor: Main.layoutManager.monitors[this._monitorIndex],
|
|
||||||
}, settings);
|
|
||||||
return this._layoutStrategy.computeLayout(this._sortedWindows);
|
|
||||||
};
|
|
||||||
/* eslint-enable no-invalid-this */
|
|
||||||
});
|
|
||||||
|
|
||||||
// position window titles on top of windows in overlay
|
let [cloneX, cloneY, cloneWidth, cloneHeight] = this._windowClone.slot;
|
||||||
this._injectionManager.overrideMethod(previewProto, '_init', originalMethod => {
|
|
||||||
/* eslint-disable no-invalid-this */
|
|
||||||
return function (...args) {
|
|
||||||
originalMethod.call(this, ...args);
|
|
||||||
|
|
||||||
if (!settings.get_boolean('window-captions-on-top'))
|
let layout = Meta.prefs_get_button_layout();
|
||||||
return;
|
let side = layout.left_buttons.indexOf(Meta.ButtonFunction.CLOSE) > -1 ? St.Side.LEFT : St.Side.RIGHT;
|
||||||
|
|
||||||
const alignConstraint = this._title.get_constraints().find(
|
let buttonX;
|
||||||
c => c.align_axis && c.align_axis === Clutter.AlignAxis.Y_AXIS);
|
let buttonY = cloneY - (button.height - button._overlap);
|
||||||
alignConstraint.factor = 0;
|
if (side == St.Side.LEFT)
|
||||||
|
buttonX = cloneX - (button.width - button._overlap);
|
||||||
|
else
|
||||||
|
buttonX = cloneX + (cloneWidth - button._overlap);
|
||||||
|
|
||||||
const bindConstraint = this._title.get_constraints().find(
|
if (animate)
|
||||||
c => c.coordinate && c.coordinate === Clutter.BindCoordinate.Y);
|
this._animateOverlayActor(button, Math.floor(buttonX), Math.floor(buttonY), button.width);
|
||||||
bindConstraint.offset = 0;
|
else
|
||||||
};
|
button.set_position(Math.floor(buttonX), Math.floor(buttonY));
|
||||||
/* eslint-enable no-invalid-this */
|
|
||||||
});
|
|
||||||
|
|
||||||
this._injectionManager.overrideMethod(previewProto, '_adjustOverlayOffsets', originalMethod => {
|
// Clutter.Actor.get_preferred_width() will return the fixed width if one
|
||||||
/* eslint-disable no-invalid-this */
|
// is set, so we need to reset the width by calling set_width(-1), to forward
|
||||||
return function (...args) {
|
// the call down to StLabel.
|
||||||
originalMethod.call(this, ...args);
|
// We also need to save and restore the current width, otherwise the animation
|
||||||
|
// starts from the wrong point.
|
||||||
|
let prevTitleWidth = title.width;
|
||||||
|
title.set_width(-1);
|
||||||
|
let [titleMinWidth, titleNatWidth] = title.get_preferred_width(-1);
|
||||||
|
let titleWidth = Math.max(titleMinWidth, Math.min(titleNatWidth, cloneWidth));
|
||||||
|
title.width = prevTitleWidth;
|
||||||
|
|
||||||
if (settings.get_boolean('window-captions-on-top'))
|
let titleX = cloneX + (cloneWidth - titleWidth) / 2;
|
||||||
this._title.translation_y = -this._title.translation_y;
|
|
||||||
};
|
|
||||||
/* eslint-enable no-invalid-this */
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
disable() {
|
/// this is the actual difference to original gnome-shell:
|
||||||
this._injectionManager.clear();
|
//let titleY = cloneY + cloneHeight + title._spacing;
|
||||||
global.stage.queue_relayout();
|
let titleY = cloneY - title.height + title._spacing;
|
||||||
|
|
||||||
|
if (animate)
|
||||||
|
this._animateOverlayActor(title, Math.floor(titleX), Math.floor(titleY), titleWidth);
|
||||||
|
else {
|
||||||
|
title.width = titleWidth;
|
||||||
|
title.set_position(Math.floor(titleX), Math.floor(titleY));
|
||||||
|
}
|
||||||
|
|
||||||
|
let borderX = cloneX - this.borderSize;
|
||||||
|
let borderY = cloneY - this.borderSize;
|
||||||
|
let borderWidth = cloneWidth + 2 * this.borderSize;
|
||||||
|
let borderHeight = cloneHeight + 2 * this.borderSize;
|
||||||
|
|
||||||
|
if (animate) {
|
||||||
|
this._animateOverlayActor(this.border, borderX, borderY,
|
||||||
|
borderWidth, borderHeight);
|
||||||
|
} else {
|
||||||
|
this.border.set_position(borderX, borderY);
|
||||||
|
this.border.set_size(borderWidth, borderHeight);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeInjection(object, injection, name) {
|
||||||
|
if (injection[name] === undefined)
|
||||||
|
delete object[name];
|
||||||
|
else
|
||||||
|
object[name] = injection[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
function disable() {
|
||||||
|
for (i in workspaceInjections)
|
||||||
|
removeInjection(Workspace.Workspace.prototype, workspaceInjections, i);
|
||||||
|
for (i in winInjections)
|
||||||
|
removeInjection(Workspace.WindowOverlay.prototype, winInjections, i);
|
||||||
|
|
||||||
|
for each (i in connectedSignals)
|
||||||
|
i.obj.disconnect(i.id);
|
||||||
|
|
||||||
|
global.stage.queue_relayout();
|
||||||
|
resetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
/* do nothing */
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
extension_data += configure_file(
|
|
||||||
input: metadata_name + '.in',
|
|
||||||
output: metadata_name,
|
|
||||||
configuration: metadata_conf
|
|
||||||
)
|
|
||||||
|
|
||||||
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"settings-schema": "@gschemaname@",
|
"settings-schema": "@gschemaname@",
|
||||||
"gettext-domain": "@gettext_domain@",
|
"gettext-domain": "@gettext_domain@",
|
||||||
"name": "Native Window Placement",
|
"name": "Native Window Placement",
|
||||||
"description": "Arrange windows in overview in a more compact way.",
|
"description": "Arrange windows in overview in a more native way",
|
||||||
"shell-version": [ "@shell_current@" ],
|
"shell-version": [ "@shell_current@" ],
|
||||||
"url": "@url@",
|
"url": "@url@",
|
||||||
"original-authors": [ "wepmaschda@gmx.de" ]
|
"original-authors": [ "wepmaschda@gmx.de" ]
|
||||||
|
|||||||
+6
-6
@@ -2,15 +2,15 @@
|
|||||||
<schema id="org.gnome.shell.extensions.native-window-placement" path="/org/gnome/shell/extensions/native-window-placement/">
|
<schema id="org.gnome.shell.extensions.native-window-placement" path="/org/gnome/shell/extensions/native-window-placement/">
|
||||||
<key name="use-more-screen" type="b">
|
<key name="use-more-screen" type="b">
|
||||||
<default>true</default>
|
<default>true</default>
|
||||||
<summary>Use more screen for windows</summary>
|
<_summary>Use more screen for windows</_summary>
|
||||||
<description>Try to use more screen for placing window thumbnails by adapting to screen aspect ratio, and consolidating
|
<_description>Try to use more screen for placing window thumbnails by adapting to screen aspect ratio, and consolidating
|
||||||
them further to reduce the bounding box. This setting applies only with the natural placement strategy.</description>
|
them further to reduce the bounding box. This setting applies only with the natural placement strategy.</_description>
|
||||||
</key>
|
</key>
|
||||||
<key name="window-captions-on-top" type="b">
|
<key name="window-captions-on-top" type="b">
|
||||||
<default>true</default>
|
<default>true</default>
|
||||||
<summary>Place window captions on top</summary>
|
<_summary>Place window captions on top</_summary>
|
||||||
<description>If true, place window captions on top the respective thumbnail, overriding shell default of placing it at
|
<_description>If true, place window captions on top the respective thumbnail, overriding shell default of placing it at
|
||||||
the bottom. Changing this setting requires restarting the shell to have any effect.</description>
|
the bottom. Changing this setting requires restarting the shell to have any effect.</_description>
|
||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
</schemalist>
|
</schemalist>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
.window-caption {
|
||||||
|
-shell-caption-spacing: 13px; /* current caption height is 26px => set it to half of it. TODO: better solution needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-picker {
|
||||||
|
-horizontal-spacing: 32px;
|
||||||
|
-vertical-spacing: 32px;
|
||||||
|
padding: 64px 32px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
EXTENSION_ID = places-menu
|
||||||
|
|
||||||
|
EXTRA_MODULES = placeDisplay.js
|
||||||
|
|
||||||
|
include ../../extension.mk
|
||||||
@@ -1,145 +1,136 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
import Clutter from 'gi://Clutter';
|
|
||||||
import GObject from 'gi://GObject';
|
|
||||||
import St from 'gi://St';
|
|
||||||
|
|
||||||
import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
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 Panel = imports.ui.panel;
|
||||||
|
|
||||||
import {PlacesManager} from './placeDisplay.js';
|
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
const N_ = function(x) { return x; }
|
||||||
|
|
||||||
const N_ = x => x;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
|
const Convenience = Me.imports.convenience;
|
||||||
|
const PlaceDisplay = Me.imports.placeDisplay;
|
||||||
|
|
||||||
const PLACE_ICON_SIZE = 16;
|
const PLACE_ICON_SIZE = 16;
|
||||||
|
|
||||||
class PlaceMenuItem extends PopupMenu.PopupBaseMenuItem {
|
const PlaceMenuItem = new Lang.Class({
|
||||||
static {
|
Name: 'PlaceMenuItem',
|
||||||
GObject.registerClass(this);
|
Extends: PopupMenu.PopupBaseMenuItem,
|
||||||
}
|
|
||||||
|
|
||||||
constructor(info) {
|
_init: function(info) {
|
||||||
super({
|
this.parent();
|
||||||
style_class: 'place-menu-item',
|
this._info = info;
|
||||||
});
|
|
||||||
this._info = info;
|
|
||||||
|
|
||||||
this._icon = new St.Icon({
|
this._icon = new St.Icon({ gicon: info.icon,
|
||||||
gicon: info.icon,
|
icon_size: PLACE_ICON_SIZE });
|
||||||
icon_size: PLACE_ICON_SIZE,
|
this.addActor(this._icon);
|
||||||
});
|
|
||||||
this.add_child(this._icon);
|
|
||||||
|
|
||||||
this._label = new St.Label({
|
this._label = new St.Label({ text: info.name });
|
||||||
text: info.name,
|
this.addActor(this._label);
|
||||||
x_expand: true,
|
|
||||||
y_align: Clutter.ActorAlign.CENTER,
|
|
||||||
});
|
|
||||||
this.add_child(this._label);
|
|
||||||
|
|
||||||
if (info.isRemovable()) {
|
this._changedId = info.connect('changed',
|
||||||
this._ejectIcon = new St.Icon({
|
Lang.bind(this, this._propertiesChanged));
|
||||||
icon_name: 'media-eject-symbolic',
|
},
|
||||||
style_class: 'popup-menu-icon',
|
|
||||||
});
|
destroy: function() {
|
||||||
this._ejectButton = new St.Button({
|
if (this._changedId) {
|
||||||
child: this._ejectIcon,
|
this._info.disconnect(this._changedId);
|
||||||
style_class: 'button',
|
this._changedId = 0;
|
||||||
});
|
|
||||||
this._ejectButton.connect('clicked', info.eject.bind(info));
|
|
||||||
this.add_child(this._ejectButton);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info.connectObject('changed',
|
this.parent();
|
||||||
this._propertiesChanged.bind(this), this);
|
},
|
||||||
}
|
|
||||||
|
|
||||||
activate(event) {
|
activate: function(event) {
|
||||||
this._info.launch(event.get_time());
|
this._info.launch(event.get_time());
|
||||||
|
|
||||||
super.activate(event);
|
this.parent(event);
|
||||||
}
|
},
|
||||||
|
|
||||||
_propertiesChanged(info) {
|
_propertiesChanged: function(info) {
|
||||||
this._icon.gicon = info.icon;
|
this._icon.gicon = info.icon;
|
||||||
this._label.text = info.name;
|
this._label.text = info.name;
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|
||||||
const SECTIONS = [
|
const SECTIONS = [
|
||||||
'special',
|
'special',
|
||||||
'devices',
|
'devices',
|
||||||
'bookmarks',
|
'bookmarks',
|
||||||
'network',
|
'network'
|
||||||
];
|
]
|
||||||
|
|
||||||
class PlacesMenu extends PanelMenu.Button {
|
const PlacesMenu = new Lang.Class({
|
||||||
static {
|
Name: 'PlacesMenu.PlacesMenu',
|
||||||
GObject.registerClass(this);
|
Extends: PanelMenu.Button,
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
_init: function() {
|
||||||
super(0.5, _('Places'));
|
let label = new St.Label({ text: _("Places") });
|
||||||
|
this.parent(0.0, label.text);
|
||||||
|
this.actor.add_actor(label);
|
||||||
|
|
||||||
let label = new St.Label({
|
this.placesManager = new PlaceDisplay.PlacesManager();
|
||||||
text: _('Places'),
|
|
||||||
y_expand: true,
|
|
||||||
y_align: Clutter.ActorAlign.CENTER,
|
|
||||||
});
|
|
||||||
this.add_actor(label);
|
|
||||||
|
|
||||||
this.placesManager = new PlacesManager();
|
this._sections = { };
|
||||||
|
|
||||||
this._sections = { };
|
for (let i=0; i < SECTIONS.length; i++) {
|
||||||
|
let id = SECTIONS[i];
|
||||||
|
this._sections[id] = new PopupMenu.PopupMenuSection();
|
||||||
|
this.placesManager.connect(id + '-updated', Lang.bind(this, function() {
|
||||||
|
this._redisplay(id);
|
||||||
|
}));
|
||||||
|
|
||||||
for (let i = 0; i < SECTIONS.length; i++) {
|
this._create(id);
|
||||||
let id = SECTIONS[i];
|
this.menu.addMenuItem(this._sections[id]);
|
||||||
this._sections[id] = new PopupMenu.PopupMenuSection();
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
this.placesManager.connect(`${id}-updated`, () => {
|
}
|
||||||
this._redisplay(id);
|
},
|
||||||
});
|
|
||||||
|
|
||||||
this._create(id);
|
destroy: function() {
|
||||||
this.menu.addMenuItem(this._sections[id]);
|
this.placesManager.destroy();
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onDestroy() {
|
this.parent();
|
||||||
this.placesManager.destroy();
|
},
|
||||||
|
|
||||||
super._onDestroy();
|
_redisplay: function(id) {
|
||||||
}
|
this._sections[id].removeAll();
|
||||||
|
|
||||||
_redisplay(id) {
|
|
||||||
this._sections[id].removeAll();
|
|
||||||
this._create(id);
|
this._create(id);
|
||||||
}
|
},
|
||||||
|
|
||||||
_create(id) {
|
_create: function(id) {
|
||||||
let places = this.placesManager.get(id);
|
let places = this.placesManager.get(id);
|
||||||
|
|
||||||
for (let i = 0; i < places.length; i++)
|
for (let i = 0; i < places.length; i++)
|
||||||
this._sections[id].addMenuItem(new PlaceMenuItem(places[i]));
|
this._sections[id].addMenuItem(new PlaceMenuItem(places[i]));
|
||||||
|
|
||||||
this._sections[id].actor.visible = places.length > 0;
|
this._sections[id].actor.visible = places.length > 0;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
Convenience.initTranslations();
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class PlacesMenuExtension extends Extension {
|
let _indicator;
|
||||||
enable() {
|
|
||||||
this._indicator = new PlacesMenu();
|
|
||||||
|
|
||||||
let pos = Main.sessionMode.panel.left.length;
|
function enable() {
|
||||||
if ('apps-menu' in Main.panel.statusArea)
|
_indicator = new PlacesMenu;
|
||||||
pos++;
|
|
||||||
Main.panel.addToStatusArea('places-menu', this._indicator, pos, 'left');
|
|
||||||
}
|
|
||||||
|
|
||||||
disable() {
|
let pos = 1;
|
||||||
this._indicator.destroy();
|
if ('apps-menu' in Main.panel.statusArea)
|
||||||
delete this._indicator;
|
pos = 2;
|
||||||
}
|
Main.panel.addToStatusArea('places-menu', _indicator, pos, 'left');
|
||||||
|
}
|
||||||
|
|
||||||
|
function disable() {
|
||||||
|
_indicator.destroy();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
extension_data += configure_file(
|
|
||||||
input: metadata_name + '.in',
|
|
||||||
output: metadata_name,
|
|
||||||
configuration: metadata_conf
|
|
||||||
)
|
|
||||||
extension_data += files('stylesheet.css')
|
|
||||||
|
|
||||||
extension_sources += files('placeDisplay.js')
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"settings-schema": "@gschemaname@",
|
"settings-schema": "@gschemaname@",
|
||||||
"gettext-domain": "@gettext_domain@",
|
"gettext-domain": "@gettext_domain@",
|
||||||
"name": "Places Status Indicator",
|
"name": "Places Status Indicator",
|
||||||
"description": "Add a menu for quickly navigating places in the system.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME's GitLab instance instead.",
|
"description": "Add a systems status menu for quickly navigating places in the system",
|
||||||
"shell-version": [ "@shell_current@" ],
|
"shell-version": [ "@shell_current@" ],
|
||||||
"url": "@url@"
|
"url": "@url@"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,244 +1,177 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
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';
|
|
||||||
|
|
||||||
import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
const DND = imports.ui.dnd;
|
||||||
import * as ShellMountOperation from 'resource:///org/gnome/shell/ui/shellMountOperation.js';
|
const Main = imports.ui.main;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const Search = imports.ui.search;
|
||||||
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
const N_ = x => x;
|
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
const N_ = function(x) { return x; }
|
||||||
|
|
||||||
Gio._promisify(Gio.AppInfo, 'launch_default_for_uri_async');
|
const Hostname1Iface = <interface name="org.freedesktop.hostname1">
|
||||||
Gio._promisify(Gio.File.prototype, 'mount_enclosing_volume');
|
<property name="PrettyHostname" type="s" access="read" />
|
||||||
|
</interface>;
|
||||||
const BACKGROUND_SCHEMA = 'org.gnome.desktop.background';
|
|
||||||
|
|
||||||
const Hostname1Iface = '<node> \
|
|
||||||
<interface name="org.freedesktop.hostname1"> \
|
|
||||||
<property name="PrettyHostname" type="s" access="read" /> \
|
|
||||||
</interface> \
|
|
||||||
</node>';
|
|
||||||
const Hostname1 = Gio.DBusProxy.makeProxyWrapper(Hostname1Iface);
|
const Hostname1 = Gio.DBusProxy.makeProxyWrapper(Hostname1Iface);
|
||||||
|
|
||||||
class PlaceInfo extends EventEmitter {
|
const PlaceInfo = new Lang.Class({
|
||||||
constructor(...params) {
|
Name: 'PlaceInfo',
|
||||||
super();
|
|
||||||
|
|
||||||
this._init(...params);
|
_init: function(kind, file, name, icon) {
|
||||||
}
|
|
||||||
|
|
||||||
_init(kind, file, name, icon) {
|
|
||||||
this.kind = kind;
|
this.kind = kind;
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.name = name || this._getFileName();
|
this.name = name || this._getFileName();
|
||||||
this.icon = icon ? new Gio.ThemedIcon({name: icon}) : this.getIcon();
|
this.icon = icon ? new Gio.ThemedIcon({ name: icon }) : this.getIcon();
|
||||||
}
|
},
|
||||||
|
|
||||||
destroy() {
|
destroy: function() {
|
||||||
}
|
},
|
||||||
|
|
||||||
isRemovable() {
|
isRemovable: function() {
|
||||||
return false;
|
return false;
|
||||||
}
|
},
|
||||||
|
|
||||||
|
launch: function(timestamp) {
|
||||||
|
let launchContext = global.create_app_launch_context();
|
||||||
|
launchContext.set_timestamp(timestamp);
|
||||||
|
|
||||||
async _ensureMountAndLaunch(context, tryMount) {
|
|
||||||
try {
|
try {
|
||||||
await Gio.AppInfo.launch_default_for_uri_async(this.file.get_uri(), context, null);
|
Gio.AppInfo.launch_default_for_uri(this.file.get_uri(),
|
||||||
} catch (err) {
|
launchContext);
|
||||||
if (!err.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED)) {
|
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED)) {
|
||||||
Main.notifyError(_('Failed to launch “%s”').format(this.name), err.message);
|
this.file.mount_enclosing_volume(0, null, null, function(file, result) {
|
||||||
return;
|
file.mount_enclosing_volume_finish(result);
|
||||||
}
|
Gio.AppInfo.launch_default_for_uri(file.get_uri(), launchContext);
|
||||||
|
|
||||||
let source = {
|
|
||||||
get_icon: () => this.icon,
|
|
||||||
};
|
|
||||||
let op = new ShellMountOperation.ShellMountOperation(source);
|
|
||||||
try {
|
|
||||||
await this.file.mount_enclosing_volume(0, op.mountOp, null);
|
|
||||||
|
|
||||||
if (tryMount)
|
|
||||||
this._ensureMountAndLaunch(context, false);
|
|
||||||
} catch (e) {
|
|
||||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
|
||||||
Main.notifyError(_('Failed to mount volume for “%s”').format(this.name), e.message);
|
|
||||||
} finally {
|
|
||||||
op.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
launch(timestamp) {
|
|
||||||
let launchContext = global.create_app_launch_context(timestamp, -1);
|
|
||||||
this._ensureMountAndLaunch(launchContext, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
getIcon() {
|
|
||||||
this.file.query_info_async('standard::symbolic-icon',
|
|
||||||
Gio.FileQueryInfoFlags.NONE,
|
|
||||||
0,
|
|
||||||
null,
|
|
||||||
(file, result) => {
|
|
||||||
try {
|
|
||||||
let info = file.query_info_finish(result);
|
|
||||||
this.icon = info.get_symbolic_icon();
|
|
||||||
this.emit('changed');
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof Gio.IOErrorEnum)
|
|
||||||
return;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
} catch(e) {
|
||||||
// return a generic icon for this kind for now, until we have the
|
Main.notifyError(_("Failed to launch \"%s\"").format(this.name), e.message);
|
||||||
// icon from the query info above
|
|
||||||
switch (this.kind) {
|
|
||||||
case 'network':
|
|
||||||
return new Gio.ThemedIcon({name: 'folder-remote-symbolic'});
|
|
||||||
case 'devices':
|
|
||||||
return new Gio.ThemedIcon({name: 'drive-harddisk-symbolic'});
|
|
||||||
case 'special':
|
|
||||||
case 'bookmarks':
|
|
||||||
default:
|
|
||||||
if (!this.file.is_native())
|
|
||||||
return new Gio.ThemedIcon({name: 'folder-remote-symbolic'});
|
|
||||||
else
|
|
||||||
return new Gio.ThemedIcon({name: 'folder-symbolic'});
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
_getFileName() {
|
getIcon: function() {
|
||||||
|
try {
|
||||||
|
let info = this.file.query_info('standard::symbolic-icon', 0, null);
|
||||||
|
return info.get_symbolic_icon();
|
||||||
|
} catch(e if e instanceof Gio.IOErrorEnum) {
|
||||||
|
// return a generic icon for this kind
|
||||||
|
switch (this.kind) {
|
||||||
|
case 'network':
|
||||||
|
return new Gio.ThemedIcon({ name: 'folder-remote-symbolic' });
|
||||||
|
case 'devices':
|
||||||
|
return new Gio.ThemedIcon({ name: 'drive-harddisk-symbolic' });
|
||||||
|
case 'special':
|
||||||
|
case 'bookmarks':
|
||||||
|
default:
|
||||||
|
if (!this.file.is_native())
|
||||||
|
return new Gio.ThemedIcon({ name: 'folder-remote-symbolic' });
|
||||||
|
else
|
||||||
|
return new Gio.ThemedIcon({ name: 'folder-symbolic' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_getFileName: function() {
|
||||||
try {
|
try {
|
||||||
let info = this.file.query_info('standard::display-name', 0, null);
|
let info = this.file.query_info('standard::display-name', 0, null);
|
||||||
return info.get_display_name();
|
return info.get_display_name();
|
||||||
} catch (e) {
|
} catch(e if e instanceof Gio.IOErrorEnum) {
|
||||||
if (e instanceof Gio.IOErrorEnum)
|
return this.file.get_basename();
|
||||||
return this.file.get_basename();
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
Signals.addSignalMethods(PlaceInfo.prototype);
|
||||||
|
|
||||||
class RootInfo extends PlaceInfo {
|
const RootInfo = new Lang.Class({
|
||||||
_init() {
|
Name: 'RootInfo',
|
||||||
super._init('devices', Gio.File.new_for_path('/'), _('Computer'));
|
Extends: PlaceInfo,
|
||||||
|
|
||||||
let busName = 'org.freedesktop.hostname1';
|
_init: function() {
|
||||||
let objPath = '/org/freedesktop/hostname1';
|
this.parent('devices', Gio.File.new_for_path('/'), _("Computer"));
|
||||||
new Hostname1(Gio.DBus.system, busName, objPath, (obj, error) => {
|
|
||||||
if (error)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._proxy = obj;
|
this._proxy = new Hostname1(Gio.DBus.system,
|
||||||
this._proxy.connect('g-properties-changed',
|
'org.freedesktop.hostname1',
|
||||||
this._propertiesChanged.bind(this));
|
'/org/freedesktop/hostname1',
|
||||||
this._propertiesChanged(obj);
|
Lang.bind(this, function(obj, error) {
|
||||||
});
|
if (error)
|
||||||
}
|
return;
|
||||||
|
|
||||||
getIcon() {
|
this._proxy.connect('g-properties-changed',
|
||||||
return new Gio.ThemedIcon({name: 'drive-harddisk-symbolic'});
|
Lang.bind(this, this._propertiesChanged));
|
||||||
}
|
this._propertiesChanged(obj);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
_propertiesChanged(proxy) {
|
getIcon: function() {
|
||||||
|
return new Gio.ThemedIcon({ name: 'drive-harddisk-symbolic' });
|
||||||
|
},
|
||||||
|
|
||||||
|
_propertiesChanged: function(proxy) {
|
||||||
// GDBusProxy will emit a g-properties-changed when hostname1 goes down
|
// GDBusProxy will emit a g-properties-changed when hostname1 goes down
|
||||||
// ignore it
|
// ignore it
|
||||||
if (proxy.g_name_owner) {
|
if (proxy.g_name_owner) {
|
||||||
this.name = proxy.PrettyHostname || _('Computer');
|
this.name = proxy.PrettyHostname || _("Computer");
|
||||||
this.emit('changed');
|
this.emit('changed');
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this._proxy.run_dispose();
|
||||||
|
this.parent();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
destroy() {
|
|
||||||
if (this._proxy) {
|
|
||||||
this._proxy.run_dispose();
|
|
||||||
this._proxy = null;
|
|
||||||
}
|
|
||||||
super.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PlaceDeviceInfo extends PlaceInfo {
|
const PlaceDeviceInfo = new Lang.Class({
|
||||||
_init(kind, mount) {
|
Name: 'PlaceDeviceInfo',
|
||||||
|
Extends: PlaceInfo,
|
||||||
|
|
||||||
|
_init: function(kind, mount) {
|
||||||
this._mount = mount;
|
this._mount = mount;
|
||||||
super._init(kind, mount.get_root(), mount.get_name());
|
this.parent(kind, mount.get_root(), mount.get_name());
|
||||||
}
|
},
|
||||||
|
|
||||||
getIcon() {
|
getIcon: function() {
|
||||||
return this._mount.get_symbolic_icon();
|
return this._mount.get_symbolic_icon();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
isRemovable() {
|
const PlaceVolumeInfo = new Lang.Class({
|
||||||
return this._mount.can_eject() || this._mount.can_unmount();
|
Name: 'PlaceVolumeInfo',
|
||||||
}
|
Extends: PlaceInfo,
|
||||||
|
|
||||||
eject() {
|
_init: function(kind, volume) {
|
||||||
let unmountArgs = [
|
|
||||||
Gio.MountUnmountFlags.NONE,
|
|
||||||
new ShellMountOperation.ShellMountOperation(this._mount).mountOp,
|
|
||||||
null, // Gio.Cancellable
|
|
||||||
];
|
|
||||||
|
|
||||||
if (this._mount.can_eject()) {
|
|
||||||
this._mount.eject_with_operation(...unmountArgs,
|
|
||||||
this._ejectFinish.bind(this));
|
|
||||||
} else {
|
|
||||||
this._mount.unmount_with_operation(...unmountArgs,
|
|
||||||
this._unmountFinish.bind(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ejectFinish(mount, result) {
|
|
||||||
try {
|
|
||||||
mount.eject_with_operation_finish(result);
|
|
||||||
} catch (e) {
|
|
||||||
this._reportFailure(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_unmountFinish(mount, result) {
|
|
||||||
try {
|
|
||||||
mount.unmount_with_operation_finish(result);
|
|
||||||
} catch (e) {
|
|
||||||
this._reportFailure(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_reportFailure(exception) {
|
|
||||||
let msg = _('Ejecting drive “%s” failed:').format(this._mount.get_name());
|
|
||||||
Main.notifyError(msg, exception.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PlaceVolumeInfo extends PlaceInfo {
|
|
||||||
_init(kind, volume) {
|
|
||||||
this._volume = volume;
|
this._volume = volume;
|
||||||
super._init(kind, volume.get_activation_root(), volume.get_name());
|
this.parent(kind, volume.get_activation_root(), volume.get_name());
|
||||||
}
|
},
|
||||||
|
|
||||||
launch(timestamp) {
|
launch: function(timestamp) {
|
||||||
if (this.file) {
|
if (this.file) {
|
||||||
super.launch(timestamp);
|
this.parent(timestamp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._volume.mount(0, null, null, (volume, result) => {
|
this._volume.mount(0, null, null, Lang.bind(this, function(volume, result) {
|
||||||
volume.mount_finish(result);
|
volume.mount_finish(result);
|
||||||
|
|
||||||
let mount = volume.get_mount();
|
let mount = volume.get_mount();
|
||||||
this.file = mount.get_root();
|
this.file = mount.get_root();
|
||||||
super.launch(timestamp);
|
this.parent(timestamp);
|
||||||
});
|
}));
|
||||||
}
|
},
|
||||||
|
|
||||||
getIcon() {
|
getIcon: function() {
|
||||||
return this._volume.get_symbolic_icon();
|
return this._volume.get_symbolic_icon();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
const DEFAULT_DIRECTORIES = [
|
const DEFAULT_DIRECTORIES = [
|
||||||
GLib.UserDirectory.DIRECTORY_DOCUMENTS,
|
GLib.UserDirectory.DIRECTORY_DOCUMENTS,
|
||||||
@@ -248,10 +181,10 @@ const DEFAULT_DIRECTORIES = [
|
|||||||
GLib.UserDirectory.DIRECTORY_VIDEOS,
|
GLib.UserDirectory.DIRECTORY_VIDEOS,
|
||||||
];
|
];
|
||||||
|
|
||||||
export class PlacesManager extends EventEmitter {
|
const PlacesManager = new Lang.Class({
|
||||||
constructor() {
|
Name: 'PlacesManager',
|
||||||
super();
|
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
this._places = {
|
this._places = {
|
||||||
special: [],
|
special: [],
|
||||||
devices: [],
|
devices: [],
|
||||||
@@ -259,131 +192,111 @@ export class PlacesManager extends EventEmitter {
|
|||||||
network: [],
|
network: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
this._settings = new Gio.Settings({schema_id: BACKGROUND_SCHEMA});
|
|
||||||
this._settings.connectObject('changed::show-desktop-icons',
|
|
||||||
() => this._updateSpecials(), this);
|
|
||||||
this._updateSpecials();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Show devices, code more or less ported from nautilus-places-sidebar.c
|
|
||||||
*/
|
|
||||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
|
||||||
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._bookmarksFile = this._findBookmarksFile();
|
|
||||||
this._bookmarkTimeoutId = 0;
|
|
||||||
this._monitor = null;
|
|
||||||
|
|
||||||
if (this._bookmarksFile) {
|
|
||||||
this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
|
|
||||||
this._monitor.connect('changed', () => {
|
|
||||||
if (this._bookmarkTimeoutId > 0)
|
|
||||||
return;
|
|
||||||
/* Defensive event compression */
|
|
||||||
this._bookmarkTimeoutId = GLib.timeout_add(
|
|
||||||
GLib.PRIORITY_DEFAULT, 100, () => {
|
|
||||||
this._bookmarkTimeoutId = 0;
|
|
||||||
this._reloadBookmarks();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this._reloadBookmarks();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this._settings?.disconnectObject(this);
|
|
||||||
this._settings = null;
|
|
||||||
|
|
||||||
this._volumeMonitor.disconnectObject(this);
|
|
||||||
|
|
||||||
if (this._monitor)
|
|
||||||
this._monitor.cancel();
|
|
||||||
if (this._bookmarkTimeoutId)
|
|
||||||
GLib.source_remove(this._bookmarkTimeoutId);
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateSpecials() {
|
|
||||||
this._places.special.forEach(p => p.destroy());
|
|
||||||
this._places.special = [];
|
|
||||||
|
|
||||||
let homePath = GLib.get_home_dir();
|
let homePath = GLib.get_home_dir();
|
||||||
|
|
||||||
this._places.special.push(new PlaceInfo(
|
this._places.special.push(new PlaceInfo('special',
|
||||||
'special',
|
Gio.File.new_for_path(homePath),
|
||||||
Gio.File.new_for_path(homePath),
|
_("Home")));
|
||||||
_('Home')));
|
|
||||||
|
|
||||||
let specials = [];
|
let specials = [];
|
||||||
let dirs = DEFAULT_DIRECTORIES.slice();
|
for (let i = 0; i < DEFAULT_DIRECTORIES.length; i++) {
|
||||||
|
let specialPath = GLib.get_user_special_dir(DEFAULT_DIRECTORIES[i]);
|
||||||
if (this._settings.get_boolean('show-desktop-icons'))
|
if (specialPath == homePath)
|
||||||
dirs.push(GLib.UserDirectory.DIRECTORY_DESKTOP);
|
|
||||||
|
|
||||||
for (let i = 0; i < dirs.length; i++) {
|
|
||||||
let specialPath = GLib.get_user_special_dir(dirs[i]);
|
|
||||||
if (!specialPath || specialPath === homePath)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
let file = Gio.File.new_for_path(specialPath), info;
|
let file = Gio.File.new_for_path(specialPath), info;
|
||||||
try {
|
try {
|
||||||
info = new PlaceInfo('special', file);
|
info = new PlaceInfo('special', file);
|
||||||
} catch (e) {
|
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) {
|
||||||
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
|
continue;
|
||||||
continue;
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
specials.push(info);
|
specials.push(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
specials.sort((a, b) => GLib.utf8_collate(a.name, b.name));
|
specials.sort(function(a, b) {
|
||||||
|
return GLib.utf8_collate(a.name, b.name);
|
||||||
|
});
|
||||||
this._places.special = this._places.special.concat(specials);
|
this._places.special = this._places.special.concat(specials);
|
||||||
|
|
||||||
this.emit('special-updated');
|
/*
|
||||||
}
|
* Show devices, code more or less ported from nautilus-places-sidebar.c
|
||||||
|
*/
|
||||||
|
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||||
|
this._connectVolumeMonitorSignals();
|
||||||
|
this._updateMounts();
|
||||||
|
|
||||||
_updateMounts() {
|
this._bookmarksFile = this._findBookmarksFile()
|
||||||
|
this._bookmarkTimeoutId = 0;
|
||||||
|
this._monitor = null;
|
||||||
|
|
||||||
|
if (this._bookmarksFile) {
|
||||||
|
this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
|
||||||
|
this._monitor.connect('changed', Lang.bind(this, function () {
|
||||||
|
if (this._bookmarkTimeoutId > 0)
|
||||||
|
return;
|
||||||
|
/* Defensive event compression */
|
||||||
|
this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () {
|
||||||
|
this._bookmarkTimeoutId = 0;
|
||||||
|
this._reloadBookmarks();
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._reloadBookmarks();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_connectVolumeMonitorSignals: function() {
|
||||||
|
const signals = ['volume-added', 'volume-removed', 'volume-changed',
|
||||||
|
'mount-added', 'mount-removed', 'mount-changed',
|
||||||
|
'drive-connected', 'drive-disconnected', 'drive-changed'];
|
||||||
|
|
||||||
|
this._volumeMonitorSignals = [];
|
||||||
|
let func = Lang.bind(this, this._updateMounts);
|
||||||
|
for (let i = 0; i < signals.length; i++) {
|
||||||
|
let id = this._volumeMonitor.connect(signals[i], func);
|
||||||
|
this._volumeMonitorSignals.push(id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
for (let i = 0; i < this._volumeMonitorSignals.length; i++)
|
||||||
|
this._volumeMonitor.disconnect(this._volumeMonitorSignals[i]);
|
||||||
|
|
||||||
|
if (this._monitor)
|
||||||
|
this._monitor.cancel();
|
||||||
|
if (this._bookmarkTimeoutId)
|
||||||
|
Mainloop.source_remove(this._bookmarkTimeoutId);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateMounts: function() {
|
||||||
let networkMounts = [];
|
let networkMounts = [];
|
||||||
let networkVolumes = [];
|
let networkVolumes = [];
|
||||||
|
|
||||||
this._places.devices.forEach(p => p.destroy());
|
this._places.devices.forEach(function (p) { p.destroy(); });
|
||||||
this._places.devices = [];
|
this._places.devices = [];
|
||||||
this._places.network.forEach(p => p.destroy());
|
this._places.network.forEach(function (p) { p.destroy(); });
|
||||||
this._places.network = [];
|
this._places.network = [];
|
||||||
|
|
||||||
/* Add standard places */
|
/* Add standard places */
|
||||||
this._places.devices.push(new RootInfo());
|
this._places.devices.push(new RootInfo());
|
||||||
this._places.network.push(new PlaceInfo(
|
this._places.network.push(new PlaceInfo('network',
|
||||||
'network',
|
Gio.File.new_for_uri('network:///'),
|
||||||
Gio.File.new_for_uri('network:///'),
|
_("Browse Network"),
|
||||||
_('Browse Network'),
|
'network-workgroup-symbolic'));
|
||||||
'network-workgroup-symbolic'));
|
|
||||||
|
|
||||||
/* first go through all connected drives */
|
/* first go through all connected drives */
|
||||||
let drives = this._volumeMonitor.get_connected_drives();
|
let drives = this._volumeMonitor.get_connected_drives();
|
||||||
for (let i = 0; i < drives.length; i++) {
|
for (let i = 0; i < drives.length; i++) {
|
||||||
let volumes = drives[i].get_volumes();
|
let volumes = drives[i].get_volumes();
|
||||||
|
|
||||||
for (let j = 0; j < volumes.length; j++) {
|
for(let j = 0; j < volumes.length; j++) {
|
||||||
let identifier = volumes[j].get_identifier('class');
|
if (volumes[j].get_identifier('class').indexOf('network') >= 0) {
|
||||||
if (identifier && identifier.includes('network')) {
|
networkVolumes.push(volumes[i]);
|
||||||
networkVolumes.push(volumes[j]);
|
|
||||||
} else {
|
} else {
|
||||||
let mount = volumes[j].get_mount();
|
let mount = volumes[j].get_mount();
|
||||||
if (mount)
|
if(mount != null)
|
||||||
this._addMount('devices', mount);
|
this._addMount('devices', mount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -391,27 +304,26 @@ export class PlacesManager extends EventEmitter {
|
|||||||
|
|
||||||
/* add all volumes that is not associated with a drive */
|
/* add all volumes that is not associated with a drive */
|
||||||
let volumes = this._volumeMonitor.get_volumes();
|
let volumes = this._volumeMonitor.get_volumes();
|
||||||
for (let i = 0; i < volumes.length; i++) {
|
for(let i = 0; i < volumes.length; i++) {
|
||||||
if (volumes[i].get_drive())
|
if(volumes[i].get_drive() != null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
let identifier = volumes[i].get_identifier('class');
|
if (volumes[i].get_identifier('class').indexOf('network') >= 0) {
|
||||||
if (identifier && identifier.includes('network')) {
|
|
||||||
networkVolumes.push(volumes[i]);
|
networkVolumes.push(volumes[i]);
|
||||||
} else {
|
} else {
|
||||||
let mount = volumes[i].get_mount();
|
let mount = volumes[i].get_mount();
|
||||||
if (mount)
|
if(mount != null)
|
||||||
this._addMount('devices', mount);
|
this._addMount('devices', mount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */
|
/* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */
|
||||||
let mounts = this._volumeMonitor.get_mounts();
|
let mounts = this._volumeMonitor.get_mounts();
|
||||||
for (let i = 0; i < mounts.length; i++) {
|
for(let i = 0; i < mounts.length; i++) {
|
||||||
if (mounts[i].is_shadowed())
|
if(mounts[i].is_shadowed())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (mounts[i].get_volume())
|
if(mounts[i].get_volume())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
let root = mounts[i].get_default_location();
|
let root = mounts[i].get_default_location();
|
||||||
@@ -431,15 +343,15 @@ export class PlacesManager extends EventEmitter {
|
|||||||
this._addVolume('network', networkVolumes[i]);
|
this._addVolume('network', networkVolumes[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < networkMounts.length; i++)
|
for (let i = 0; i < networkMounts.length; i++) {
|
||||||
this._addMount('network', networkMounts[i]);
|
this._addMount('network', networkMounts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
this.emit('devices-updated');
|
this.emit('devices-updated');
|
||||||
this.emit('network-updated');
|
this.emit('network-updated');
|
||||||
}
|
},
|
||||||
|
|
||||||
_findBookmarksFile() {
|
_findBookmarksFile: function() {
|
||||||
let paths = [
|
let paths = [
|
||||||
GLib.build_filenamev([GLib.get_user_config_dir(), 'gtk-3.0', 'bookmarks']),
|
GLib.build_filenamev([GLib.get_user_config_dir(), 'gtk-3.0', 'bookmarks']),
|
||||||
GLib.build_filenamev([GLib.get_home_dir(), '.gtk-bookmarks']),
|
GLib.build_filenamev([GLib.get_home_dir(), '.gtk-bookmarks']),
|
||||||
@@ -451,9 +363,10 @@ export class PlacesManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
},
|
||||||
|
|
||||||
|
_reloadBookmarks: function() {
|
||||||
|
|
||||||
_reloadBookmarks() {
|
|
||||||
this._bookmarks = [];
|
this._bookmarks = [];
|
||||||
|
|
||||||
let content = Shell.get_file_contents_utf8_sync(this._bookmarksFile.get_path());
|
let content = Shell.get_file_contents_utf8_sync(this._bookmarksFile.get_path());
|
||||||
@@ -463,7 +376,7 @@ export class PlacesManager extends EventEmitter {
|
|||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
let line = lines[i];
|
let line = lines[i];
|
||||||
let components = line.split(' ');
|
let components = line.split(' ');
|
||||||
let [bookmark] = components;
|
let bookmark = components[0];
|
||||||
|
|
||||||
if (!bookmark)
|
if (!bookmark)
|
||||||
continue;
|
continue;
|
||||||
@@ -473,16 +386,16 @@ export class PlacesManager extends EventEmitter {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
let duplicate = false;
|
let duplicate = false;
|
||||||
for (let j = 0; j < this._places.special.length; j++) {
|
for (let i = 0; i < this._places.special.length; i++) {
|
||||||
if (file.equal(this._places.special[j].file)) {
|
if (file.equal(this._places.special[i].file)) {
|
||||||
duplicate = true;
|
duplicate = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (duplicate)
|
if (duplicate)
|
||||||
continue;
|
continue;
|
||||||
for (let j = 0; j < bookmarks.length; j++) {
|
for (let i = 0; i < bookmarks.length; i++) {
|
||||||
if (file.equal(bookmarks[j].file)) {
|
if (file.equal(bookmarks[i].file)) {
|
||||||
duplicate = true;
|
duplicate = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -500,37 +413,34 @@ export class PlacesManager extends EventEmitter {
|
|||||||
this._places.bookmarks = bookmarks;
|
this._places.bookmarks = bookmarks;
|
||||||
|
|
||||||
this.emit('bookmarks-updated');
|
this.emit('bookmarks-updated');
|
||||||
}
|
},
|
||||||
|
|
||||||
_addMount(kind, mount) {
|
_addMount: function(kind, mount) {
|
||||||
let devItem;
|
let devItem;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
devItem = new PlaceDeviceInfo(kind, mount);
|
devItem = new PlaceDeviceInfo(kind, mount);
|
||||||
} catch (e) {
|
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) {
|
||||||
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
|
return;
|
||||||
return;
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._places[kind].push(devItem);
|
this._places[kind].push(devItem);
|
||||||
}
|
},
|
||||||
|
|
||||||
_addVolume(kind, volume) {
|
_addVolume: function(kind, volume) {
|
||||||
let volItem;
|
let volItem;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
volItem = new PlaceVolumeInfo(kind, volume);
|
volItem = new PlaceVolumeInfo(kind, volume);
|
||||||
} catch (e) {
|
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) {
|
||||||
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
|
return;
|
||||||
return;
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._places[kind].push(volItem);
|
this._places[kind].push(volItem);
|
||||||
}
|
},
|
||||||
|
|
||||||
get(kind) {
|
get: function (kind) {
|
||||||
return this._places[kind];
|
return this._places[kind];
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
Signals.addSignalMethods(PlacesManager.prototype);
|
||||||
|
|||||||
@@ -1,8 +1 @@
|
|||||||
.place-menu-item .button {
|
/* none used*/
|
||||||
border-radius: 99px;
|
|
||||||
padding: 3px;
|
|
||||||
min-height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.place-menu-item .button:ltr { margin-left: 6px; }
|
|
||||||
.place-menu-item .button:rtl { margin-right: 6px; }
|
|
||||||
|
|||||||
@@ -1,168 +0,0 @@
|
|||||||
/* Screenshot Window Sizer for Gnome Shell
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013 Owen Taylor <otaylor@redhat.com>
|
|
||||||
* Copyright (c) 2013 Richard Hughes <richard@hughsie.com>
|
|
||||||
*
|
|
||||||
* 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, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Clutter from 'gi://Clutter';
|
|
||||||
import Meta from 'gi://Meta';
|
|
||||||
import Shell from 'gi://Shell';
|
|
||||||
import St from 'gi://St';
|
|
||||||
|
|
||||||
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
|
|
||||||
|
|
||||||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
|
||||||
|
|
||||||
const MESSAGE_FADE_TIME = 2000;
|
|
||||||
|
|
||||||
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) {
|
|
||||||
if (!this._text) {
|
|
||||||
this._text = new St.Label({style_class: 'screenshot-sizer-message'});
|
|
||||||
Main.uiGroup.add_actor(this._text);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_hideMessage() {
|
|
||||||
this._text.destroy();
|
|
||||||
delete this._text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Meta.Display} display - the display
|
|
||||||
* @param {Meta.Window=} window - for per-window bindings, the window
|
|
||||||
* @param {Meta.KeyBinding} binding - the key binding
|
|
||||||
*/
|
|
||||||
_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 = this.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the next size up or down from ideal
|
|
||||||
let newIndex = (nearestIndex + (backwards ? -1 : 1)) % scaledSizes.length;
|
|
||||||
let [newWidth, newHeight] = scaledSizes[newIndex];
|
|
||||||
|
|
||||||
// Push the window onscreen if it would be resized offscreen
|
|
||||||
let newX = outerRect.x;
|
|
||||||
let newY = outerRect.y;
|
|
||||||
if (newX + newWidth > workArea.x + workArea.width)
|
|
||||||
newX = Math.max(workArea.x + workArea.width - newWidth);
|
|
||||||
if (newY + newHeight > workArea.y + workArea.height)
|
|
||||||
newY = Math.max(workArea.y + workArea.height - newHeight);
|
|
||||||
|
|
||||||
const id = window.connect('size-changed', () => {
|
|
||||||
window.disconnect(id);
|
|
||||||
this._notifySizeChange(window);
|
|
||||||
});
|
|
||||||
window.move_resize_frame(true, newX, newY, newWidth, newHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
extension_data += configure_file(
|
|
||||||
input: metadata_name + '.in',
|
|
||||||
output: metadata_name,
|
|
||||||
configuration: metadata_conf
|
|
||||||
)
|
|
||||||
extension_data += files('stylesheet.css')
|
|
||||||
|
|
||||||
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"extension-id": "@extension_id@",
|
|
||||||
"uuid": "@uuid@",
|
|
||||||
"settings-schema": "@gschemaname@",
|
|
||||||
"gettext-domain": "@gettext_domain@",
|
|
||||||
"name": "Screenshot Window Sizer",
|
|
||||||
"description": "Resize windows for GNOME Software screenshots",
|
|
||||||
"shell-version": [ "@shell_current@" ],
|
|
||||||
"url": "@url@"
|
|
||||||
}
|
|
||||||
-14
@@ -1,14 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<schemalist gettext-domain="gnome-shell-extensions">
|
|
||||||
<schema id="org.gnome.shell.extensions.screenshot-window-sizer"
|
|
||||||
path="/org/gnome/shell/extensions/screenshot-window-sizer/">
|
|
||||||
<key type="as" name="cycle-screenshot-sizes">
|
|
||||||
<default><![CDATA[['<Alt><Control>s']]]></default>
|
|
||||||
<summary>Cycle Screenshot Sizes</summary>
|
|
||||||
</key>
|
|
||||||
<key type="as" name="cycle-screenshot-sizes-backward">
|
|
||||||
<default><![CDATA[['<Shift><Alt><Control>s']]]></default>
|
|
||||||
<summary>Cycle Screenshot Sizes Backward</summary>
|
|
||||||
</key>
|
|
||||||
</schema>
|
|
||||||
</schemalist>
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
EXTENSION_ID = systemMonitor
|
||||||
|
|
||||||
|
include ../../extension.mk
|
||||||
@@ -0,0 +1,360 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const GTop = imports.gi.GTop;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
|
||||||
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
|
const Convenience = Me.imports.convenience;
|
||||||
|
|
||||||
|
const INDICATOR_UPDATE_INTERVAL = 500;
|
||||||
|
const INDICATOR_NUM_GRID_LINES = 3;
|
||||||
|
|
||||||
|
const ITEM_LABEL_SHOW_TIME = 0.15;
|
||||||
|
const ITEM_LABEL_HIDE_TIME = 0.1;
|
||||||
|
const ITEM_HOVER_TIMEOUT = 300;
|
||||||
|
|
||||||
|
const Indicator = new Lang.Class({
|
||||||
|
Name: 'SystemMonitor.Indicator',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._initValues();
|
||||||
|
this.drawing_area = new St.DrawingArea({ reactive: true });
|
||||||
|
this.drawing_area.connect('repaint', Lang.bind(this, this._draw));
|
||||||
|
this.drawing_area.connect('button-press-event', function() {
|
||||||
|
let app = Shell.AppSystem.get_default().lookup_app('gnome-system-monitor.desktop');
|
||||||
|
app.open_new_window(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.actor = new St.Bin({ style_class: "extension-systemMonitor-indicator-area",
|
||||||
|
reactive: true, track_hover: true,
|
||||||
|
x_fill: true, y_fill: true });
|
||||||
|
this.actor.add_actor(this.drawing_area);
|
||||||
|
|
||||||
|
this._timeout = Mainloop.timeout_add(INDICATOR_UPDATE_INTERVAL, Lang.bind(this, function () {
|
||||||
|
this._updateValues();
|
||||||
|
this.drawing_area.queue_repaint();
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
showLabel: function() {
|
||||||
|
if (this.label == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.label.opacity = 0;
|
||||||
|
this.label.show();
|
||||||
|
|
||||||
|
let [stageX, stageY] = this.actor.get_transformed_position();
|
||||||
|
|
||||||
|
let itemWidth = this.actor.allocation.x2 - this.actor.allocation.x1;
|
||||||
|
let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
|
||||||
|
|
||||||
|
let labelWidth = this.label.width;
|
||||||
|
let labelHeight = this.label.height;
|
||||||
|
let xOffset = Math.floor((itemWidth - labelWidth) / 2)
|
||||||
|
|
||||||
|
let x = stageX + xOffset;
|
||||||
|
|
||||||
|
let node = this.label.get_theme_node();
|
||||||
|
let yOffset = node.get_length('-y-offset');
|
||||||
|
|
||||||
|
let y = stageY - this.label.get_height() - yOffset;
|
||||||
|
|
||||||
|
this.label.set_position(x, y);
|
||||||
|
Tweener.addTween(this.label,
|
||||||
|
{ opacity: 255,
|
||||||
|
time: ITEM_LABEL_SHOW_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setLabelText: function(text) {
|
||||||
|
if (this.label == null)
|
||||||
|
this.label = new St.Label({ style_class: 'extension-systemMonitor-indicator-label'});
|
||||||
|
|
||||||
|
this.label.set_text(text);
|
||||||
|
Main.layoutManager.addChrome(this.label);
|
||||||
|
this.label.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
hideLabel: function () {
|
||||||
|
Tweener.addTween(this.label,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: ITEM_LABEL_HIDE_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: Lang.bind(this, function() {
|
||||||
|
this.label.hide();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
Mainloop.source_remove(this._timeout);
|
||||||
|
|
||||||
|
this.actor.destroy();
|
||||||
|
if (this.label)
|
||||||
|
this.label.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
_initValues: function() {
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateValues: function() {
|
||||||
|
},
|
||||||
|
|
||||||
|
_draw: function(area) {
|
||||||
|
let [width, height] = area.get_surface_size();
|
||||||
|
let themeNode = this.actor.get_theme_node();
|
||||||
|
let cr = area.get_context();
|
||||||
|
|
||||||
|
//draw the background grid
|
||||||
|
let color = themeNode.get_color(this.gridColor);
|
||||||
|
let gridOffset = Math.floor(height / (INDICATOR_NUM_GRID_LINES + 1));
|
||||||
|
for (let i = 1; i <= INDICATOR_NUM_GRID_LINES; ++i) {
|
||||||
|
cr.moveTo(0, i * gridOffset + .5);
|
||||||
|
cr.lineTo(width, i * gridOffset + .5);
|
||||||
|
}
|
||||||
|
Clutter.cairo_set_source_color(cr, color);
|
||||||
|
cr.setLineWidth(1);
|
||||||
|
cr.setDash([4,1], 0);
|
||||||
|
cr.stroke();
|
||||||
|
|
||||||
|
//draw the foreground
|
||||||
|
|
||||||
|
function makePath(values, reverse, nudge) {
|
||||||
|
if (nudge == null) {
|
||||||
|
nudge = 0;
|
||||||
|
}
|
||||||
|
//if we are going in reverse, we are completing the bottom of a chart, so use lineTo
|
||||||
|
if (reverse) {
|
||||||
|
cr.lineTo(values.length - 1, (1 - values[values.length - 1]) * height + nudge);
|
||||||
|
for (let k = values.length - 2; k >= 0; --k) {
|
||||||
|
cr.lineTo(k, (1 - values[k]) * height + nudge);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cr.moveTo(0, (1 - values[0]) * height + nudge);
|
||||||
|
for (let k = 1; k < values.length; ++k) {
|
||||||
|
cr.lineTo(k, (1 - values[k]) * height + nudge);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let renderStats = this.renderStats;
|
||||||
|
|
||||||
|
// Make sure we don't have more sample points than pixels
|
||||||
|
renderStats.map(Lang.bind(this, function(k){
|
||||||
|
let stat = this.stats[k];
|
||||||
|
if (stat.values.length > width) {
|
||||||
|
stat.values = stat.values.slice(stat.values.length - width, stat.values.length);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
for (let i = 0; i < renderStats.length; ++i) {
|
||||||
|
let stat = this.stats[renderStats[i]];
|
||||||
|
// We outline at full opacity and fill with 40% opacity
|
||||||
|
let outlineColor = themeNode.get_color(stat.color);
|
||||||
|
let color = new Clutter.Color(outlineColor);
|
||||||
|
color.alpha = color.alpha * .4;
|
||||||
|
|
||||||
|
// Render the background between us and the next level
|
||||||
|
makePath(stat.values, false);
|
||||||
|
// If there is a process below us, render the cpu between us and it, otherwise,
|
||||||
|
// render to the bottom of the chart
|
||||||
|
if (i == renderStats.length - 1) {
|
||||||
|
cr.lineTo(stat.values.length - 1, height);
|
||||||
|
cr.lineTo(0, height);
|
||||||
|
cr.closePath();
|
||||||
|
} else {
|
||||||
|
let nextStat = this.stats[renderStats[i+1]];
|
||||||
|
makePath(nextStat.values, true);
|
||||||
|
}
|
||||||
|
cr.closePath()
|
||||||
|
Clutter.cairo_set_source_color(cr, color);
|
||||||
|
cr.fill();
|
||||||
|
|
||||||
|
// Render the outline of this level
|
||||||
|
makePath(stat.values, false, .5);
|
||||||
|
Clutter.cairo_set_source_color(cr, outlineColor);
|
||||||
|
cr.setLineWidth(1.0);
|
||||||
|
cr.setDash([], 0);
|
||||||
|
cr.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const CpuIndicator = new Lang.Class({
|
||||||
|
Name: 'SystemMonitor.CpuIndicator',
|
||||||
|
Extends: Indicator,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.parent();
|
||||||
|
|
||||||
|
this.gridColor = '-grid-color';
|
||||||
|
this.renderStats = [ 'cpu-user', 'cpu-sys', 'cpu-iowait' ];
|
||||||
|
|
||||||
|
// Make sure renderStats is sorted as necessary for rendering
|
||||||
|
let renderStatOrder = {'cpu-total': 0, 'cpu-user': 1, 'cpu-sys': 2, 'cpu-iowait': 3};
|
||||||
|
this.renderStats = this.renderStats.sort(function(a,b) {
|
||||||
|
return renderStatOrder[a] - renderStatOrder[b];
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setLabelText(_("CPU"));
|
||||||
|
},
|
||||||
|
|
||||||
|
_initValues: function() {
|
||||||
|
this._prev = new GTop.glibtop_cpu;
|
||||||
|
GTop.glibtop_get_cpu(this._prev);
|
||||||
|
|
||||||
|
this.stats = {
|
||||||
|
'cpu-user': {color: '-cpu-user-color', values: []},
|
||||||
|
'cpu-sys': {color: '-cpu-sys-color', values: []},
|
||||||
|
'cpu-iowait': {color: '-cpu-iowait-color', values: []},
|
||||||
|
'cpu-total': {color: '-cpu-total-color', values: []}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateValues: function() {
|
||||||
|
let cpu = new GTop.glibtop_cpu;
|
||||||
|
let t = 0.0;
|
||||||
|
GTop.glibtop_get_cpu(cpu);
|
||||||
|
let total = cpu.total - this._prev.total;
|
||||||
|
let user = cpu.user - this._prev.user;
|
||||||
|
let sys = cpu.sys - this._prev.sys;
|
||||||
|
let iowait = cpu.iowait - this._prev.iowait;
|
||||||
|
let idle = cpu.idle - this._prev.idle;
|
||||||
|
|
||||||
|
t += iowait / total;
|
||||||
|
this.stats['cpu-iowait'].values.push(t);
|
||||||
|
t += sys / total;
|
||||||
|
this.stats['cpu-sys'].values.push(t);
|
||||||
|
t += user / total;
|
||||||
|
this.stats['cpu-user'].values.push(t);
|
||||||
|
this.stats['cpu-total'].values.push(1 - idle / total);
|
||||||
|
|
||||||
|
this._prev = cpu;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const MemoryIndicator = new Lang.Class({
|
||||||
|
Name: 'SystemMonitor.MemoryIndicator',
|
||||||
|
Extends: Indicator,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.parent();
|
||||||
|
|
||||||
|
this.gridColor = '-grid-color';
|
||||||
|
this.renderStats = [ 'mem-user', 'mem-other', 'mem-cached' ];
|
||||||
|
|
||||||
|
// Make sure renderStats is sorted as necessary for rendering
|
||||||
|
let renderStatOrder = { 'mem-cached': 0, 'mem-other': 1, 'mem-user': 2 };
|
||||||
|
this.renderStats = this.renderStats.sort(function(a,b) {
|
||||||
|
return renderStatOrder[a] - renderStatOrder[b];
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setLabelText(_("Memory"));
|
||||||
|
},
|
||||||
|
|
||||||
|
_initValues: function() {
|
||||||
|
this.mem = new GTop.glibtop_mem;
|
||||||
|
this.stats = {
|
||||||
|
'mem-user': { color: "-mem-user-color", values: [] },
|
||||||
|
'mem-other': { color: "-mem-other-color", values: [] },
|
||||||
|
'mem-cached': { color: "-mem-cached-color", values: [] }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateValues: function() {
|
||||||
|
GTop.glibtop_get_mem(this.mem);
|
||||||
|
|
||||||
|
let t = this.mem.user / this.mem.total;
|
||||||
|
this.stats['mem-user'].values.push(t);
|
||||||
|
t += (this.mem.used - this.mem.user - this.mem.cached) / this.mem.total;
|
||||||
|
this.stats['mem-other'].values.push(t);
|
||||||
|
t += this.mem.cached / this.mem.total;
|
||||||
|
this.stats['mem-cached'].values.push(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const INDICATORS = [CpuIndicator, MemoryIndicator];
|
||||||
|
|
||||||
|
const Extension = new Lang.Class({
|
||||||
|
Name: 'SystemMonitor.Extension',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
Convenience.initTranslations();
|
||||||
|
|
||||||
|
this._showLabelTimeoutId = 0;
|
||||||
|
this._resetHoverTimeoutId = 0;
|
||||||
|
this._labelShowing = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
enable: function() {
|
||||||
|
this._box = new St.BoxLayout({ style_class: 'extension-systemMonitor-container',
|
||||||
|
x_align: Clutter.ActorAlign.START,
|
||||||
|
x_expand: true });
|
||||||
|
this._indicators = [ ];
|
||||||
|
|
||||||
|
for (let i = 0; i < INDICATORS.length; i++) {
|
||||||
|
let indicator = new (INDICATORS[i])();
|
||||||
|
|
||||||
|
indicator.actor.connect('notify::hover', Lang.bind(this, function() {
|
||||||
|
this._onHover(indicator);
|
||||||
|
}));
|
||||||
|
this._box.add_actor(indicator.actor);
|
||||||
|
this._indicators.push(indicator);
|
||||||
|
}
|
||||||
|
|
||||||
|
Main.messageTray.actor.add_actor(this._box);
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
this._indicators.forEach(function(i) { i.destroy(); });
|
||||||
|
this._box.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onHover: function (item) {
|
||||||
|
if (item.actor.get_hover()) {
|
||||||
|
if (this._showLabelTimeoutId == 0) {
|
||||||
|
let timeout = this._labelShowing ? 0 : ITEM_HOVER_TIMEOUT;
|
||||||
|
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._labelShowing = true;
|
||||||
|
item.showLabel();
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
if (this._resetHoverTimeoutId > 0) {
|
||||||
|
Mainloop.source_remove(this._resetHoverTimeoutId);
|
||||||
|
this._resetHoverTimeoutId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this._showLabelTimeoutId > 0)
|
||||||
|
Mainloop.source_remove(this._showLabelTimeoutId);
|
||||||
|
this._showLabelTimeoutId = 0;
|
||||||
|
item.hideLabel();
|
||||||
|
if (this._labelShowing) {
|
||||||
|
this._resetHoverTimeoutId = Mainloop.timeout_add(ITEM_HOVER_TIMEOUT,
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._labelShowing = false;
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
return new Extension();
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"shell-version": ["@shell_current@" ],
|
||||||
|
"uuid": "@uuid@",
|
||||||
|
"extension-id": "@extension_id@",
|
||||||
|
"settings-schema": "@gschemaname@",
|
||||||
|
"gettext-domain": "@gettext_domain@",
|
||||||
|
"original-author": "zaspire@rambler.ru",
|
||||||
|
"name": "SystemMonitor",
|
||||||
|
"description": "System Monitor",
|
||||||
|
"url": "@url@"
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
.extension-systemMonitor-container {
|
||||||
|
spacing: 5px;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extension-systemMonitor-indicator-area {
|
||||||
|
border: 1px solid #8d8d8d;
|
||||||
|
border-radius: 3px;
|
||||||
|
width: 100px;
|
||||||
|
/* message tray is 72px, so 20px padding of the container,
|
||||||
|
2px of border, makes it 50px */
|
||||||
|
height: 50px;
|
||||||
|
-grid-color: #575757;
|
||||||
|
-cpu-total-color: rgb(0,154,62);
|
||||||
|
-cpu-user-color: rgb(69,154,0);
|
||||||
|
-cpu-sys-color: rgb(255,253,81);
|
||||||
|
-cpu-iowait-color: rgb(210,148,0);
|
||||||
|
-mem-user-color: rgb(210,148,0);
|
||||||
|
-mem-cached-color: rgb(90,90,90);
|
||||||
|
-mem-other-color: rgb(205,203,41);
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extension-systemMonitor-indicator-label {
|
||||||
|
border-radius: 7px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
background-color: rgba(0,0,0,0.9);
|
||||||
|
text-align: center;
|
||||||
|
-y-offset: 8px;
|
||||||
|
font-size: 9pt;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
EXTENSION_ID = user-theme
|
||||||
|
|
||||||
|
include ../../extension.mk
|
||||||
|
include ../../settings.mk
|
||||||
|
|
||||||
@@ -1,53 +1,70 @@
|
|||||||
// -*- 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 ~/.themes/name/gnome-shell
|
||||||
|
|
||||||
import Gio from 'gi://Gio';
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
|
const Lang = imports.lang;
|
||||||
|
const Main = imports.ui.main;
|
||||||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
|
||||||
|
|
||||||
import {getThemeDirs, getModeThemeDirs} from './util.js';
|
|
||||||
|
|
||||||
const SETTINGS_KEY = 'name';
|
const SETTINGS_KEY = 'name';
|
||||||
|
|
||||||
export default class ThemeManager extends Extension {
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
enable() {
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
this._settings = this.getSettings();
|
const Convenience = Me.imports.convenience;
|
||||||
this._settings.connect(`changed::${SETTINGS_KEY}`, this._changeTheme.bind(this));
|
|
||||||
this._changeTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
disable() {
|
const ThemeManager = new Lang.Class({
|
||||||
this._settings?.run_dispose();
|
Name: 'UserTheme.ThemeManager',
|
||||||
this._settings = null;
|
|
||||||
|
_init: function() {
|
||||||
|
this._settings = Convenience.getSettings();
|
||||||
|
},
|
||||||
|
|
||||||
|
enable: function() {
|
||||||
|
this._changedId = this._settings.connect('changed::'+SETTINGS_KEY, Lang.bind(this, this._changeTheme));
|
||||||
|
this._changeTheme();
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
if (this._changedId) {
|
||||||
|
this._settings.disconnect(this._changedId);
|
||||||
|
this._changedId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
Main.setThemeStylesheet(null);
|
Main.setThemeStylesheet(null);
|
||||||
Main.loadTheme();
|
Main.loadTheme();
|
||||||
}
|
},
|
||||||
|
|
||||||
_changeTheme() {
|
_changeTheme: function() {
|
||||||
let stylesheet = null;
|
let _stylesheet = null;
|
||||||
let themeName = this._settings.get_string(SETTINGS_KEY);
|
let _themeName = this._settings.get_string(SETTINGS_KEY);
|
||||||
|
|
||||||
if (themeName) {
|
if (_themeName) {
|
||||||
const stylesheetPaths = getThemeDirs()
|
let _userCssStylesheet = GLib.get_home_dir() + '/.themes/' + _themeName + '/gnome-shell/gnome-shell.css';
|
||||||
.map(dir => `${dir}/${themeName}/gnome-shell/gnome-shell.css`);
|
let file = Gio.file_new_for_path(_userCssStylesheet);
|
||||||
|
if (file.query_exists(null))
|
||||||
stylesheetPaths.push(...getModeThemeDirs()
|
_stylesheet = _userCssStylesheet;
|
||||||
.map(dir => `${dir}/${themeName}.css`));
|
else {
|
||||||
|
let sysdirs = GLib.get_system_data_dirs();
|
||||||
stylesheet = stylesheetPaths.find(path => {
|
for (let i = 0; i < sysdirs.length; i++) {
|
||||||
let file = Gio.file_new_for_path(path);
|
_userCssStylesheet = sysdirs[i] + '/themes/' + _themeName + '/gnome-shell/gnome-shell.css';
|
||||||
return file.query_exists(null);
|
let file = Gio.file_new_for_path(_userCssStylesheet);
|
||||||
});
|
if (file.query_exists(null)) {
|
||||||
|
_stylesheet = _userCssStylesheet;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stylesheet)
|
if (_stylesheet)
|
||||||
log(`loading user theme: ${stylesheet}`);
|
global.log('loading user theme: ' + _stylesheet);
|
||||||
else
|
else
|
||||||
log('loading default theme (Adwaita)');
|
global.log('loading default theme (Adwaita)');
|
||||||
Main.setThemeStylesheet(stylesheet);
|
Main.setThemeStylesheet(_stylesheet);
|
||||||
Main.loadTheme();
|
Main.loadTheme();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
return new ThemeManager();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
extension_data += configure_file(
|
|
||||||
input: metadata_name + '.in',
|
|
||||||
output: metadata_name,
|
|
||||||
configuration: metadata_conf
|
|
||||||
)
|
|
||||||
|
|
||||||
extension_sources += files('prefs.js', 'util.js')
|
|
||||||
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user