Compare commits

..

3 Commits

Author SHA1 Message Date
Giovanni Campagna
3c100c393a Bump version to 3.8.2
To go along GNOME Shell 3.8.2
2013-05-14 07:16:36 +02:00
Florian Müllner
2e1856ab34 window-list: Make bottom panel taller in classic mode
Consider additional borders in classic styling for the panel height.

https://bugzilla.gnome.org/show_bug.cgi?id=698169
2013-05-13 22:02:27 +02:00
Florian Müllner
0861745de4 window-list: Use notify::n-workspaces to recreate workspace menu
When the number of workspaces changes, we clear the existing menu
and recreate it based on the screen::n-workspaces property, so it
is slightly more correct to track changes to the property directly
instead of using the workspace-added/workspace-removed signals.
This change also fixes a corner case, where changes to the property
before workspaces are initialized are missed and we end up with the
wrong number of workspaces.

https://bugzilla.gnome.org/show_bug.cgi?id=699350
2013-04-30 21:54:54 +02:00
201 changed files with 11679 additions and 28406 deletions

16
.gitignore vendored
View File

@@ -1,22 +1,14 @@
ABOUT-NLS
Makefile
Makefile.in
Makefile.in.in
aclocal.m4
autom4te.cache/
config/
configure
config.log
config.status
aclocal.m4
autom4te.cache/
data/*.json
m4/
po/*.header
po/*.sed
po/*.sin
po/Makevars.template
po/POTFILES
po/Rules-quot
po/gnome-shell-extensions.pot
po/POTFILES
po/stamp-it
staging/
zip-files/
@@ -25,5 +17,7 @@ zip-files/
*.gmo
metadata.json
*.desktop
*.desktop.in
*.gschema.xml
*.gschema.valid
*.session

View File

@@ -1,13 +0,0 @@
image: fedora:latest
stages:
- build
before_script:
- dnf install -y meson gettext mozjs52-devel
build-shell-extensions:
stage: build
script:
- meson _build .
- ninja -C _build test install

3
.gitmodules vendored
View File

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

31
HACKING Normal file
View File

@@ -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.

View File

@@ -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/master/doc/Style_Guide.md

52
Makefile.am Normal file
View File

@@ -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

524
NEWS
View File

@@ -1,525 +1,7 @@
3.31.2
======
* Remove obsolete alternate-tab extension [Florian; #786496]
* Adjust to gnome-shell changes [Florian; #113]
Contributors:
Florian Müllner
3.30.1
======
* apps-menu: Fix height on HiDPI systems [Florian; #102]
* window-list: Only switch between windows on active workspace when scrolling
[Florian; #78]
Contributors:
Florian Müllner
3.30.0
======
* Bump version
3.29.91
=======
* Misc. bug fixes [Florian; #90]
Contributors:
Florian Müllner
3.29.90
=======
* Misc. bug fixes [Florian; #786496]
Contributors:
Florian Müllner
3.29.3
======
* Adjust to global.screen removal [Jonas; #759538]
Contributors:
Jonas Ådahl, Florian Müllner
3.29.2
======
* Misc. bug fixes [Florian; #69]
Contributors:
Florian Müllner
3.28.1
======
* Misc. bug fixes [Xiaoguang, Florian; #59, #62]
Contributors:
Florian Müllner, Xiaoguang Wang
Translators:
Dz Chen [zh_CN]
3.28.0
======
Contributors:
Florian Müllner, Xiaoguang Wang
Translators:
Aman Alam [pa], Bruce Cowan [en_GB]
3.27.92
=======
Contributors:
Florian Müllner
Translators:
Piotr Drąg [es], GNOME Translation Robot [gd], Daniel Șerbănescu [ro]
3.27.91
=======
* places-menu: Support unmounting ejectable places [Rémy; #17]
* apps-menu: Support separators and custom sort order [Florian; #27]
* Port to meson [Florian; #31, #45]
* window-list: Fix missing icons on wayland [Florian; #10]
* places-menu: Fix terminating gnome-shell with recent gjs [Florian; #44]
* auto-move: Make it work with wayland windows [Florian; #33]
* Classic theme fixes [Florian, Jonas; #26, #41, #39, #40]
* Require sassc for classic styling [Florian; !28]
* Misc. bug fixes [Piotr, Florian; #772211, #32, #30]
Contributors:
Jeremy Bicha, Piotr Drąg, Jonas Kümmerlin, Rémy Lefevre, Iñigo Martínez,
Florian Müllner
Translators:
Matej Urbančič [sl], Kjartan Maraas [nb]
3.27.1
======
* updated translations (ca@valencia)
3.26.1
======
* native-window-placement: Adjust to gnome-shell changes
* updated translations: el, fa, ru, sv
3.26.0
======
* updated translations (be, bg, ca, da, eu, fi, is, it, ko, lv, ml,
nl, pt_BR, vi, zh_TW)
3.25.91
=======
* updated translations (ca, fr, it, pl, pt_BR, sr, sr@latin, tr)
3.25.90
=======
* updated translations (es, gl, hr, hu, kk, sl, sv, sv)
3.25.4
======
* screenshot-window-sizer: Fix backward cycling
* updated translations (ar, be, ca, cs, de, fur, id, lt, pl, sk)
3.25.3
======
* places-menu: Use mount operation if necessary
* window-list: Respect MWM hints
* updated translations (es, fur, kk)
3.25.2
======
* places-menu: Make URI launching asynchronous
* updated translations (de, fur, hr, hu, id, sl)
3.25.1
======
* apps-menu: Mark copied launchers as trusted
* places-menu: Make icon lookup asynchronous
* updated translations (hr)
3.24.1
======
* apps-menu: Allow creating desktop launchers via DND
* updated translations (el, vi)
3.24.0
======
* updated translations (lv, tr)
3.23.92
=======
* update classic theme
* updated translations (be, ko, ca, da, cs, ru, lt)
3.23.91
=======
* updated translations (de, es, eu, fi, fr, fur, gl, hu, id, it, kk, nb, pl, pt_BR,
sk, sr, sr@latin, sv, uk, zh_TW)
3.23.90
=======
* window-list: Improve styling
* window-list: Hide workspace indicator when there's a single (static) workspace
* new translation (be)
3.23.2
======
* alternateTab: Don't take over 'switch-group' shortcut
* updated translations (zh_CN)
3.22.1
======
* window-list: Update icon on app changes
3.22.0
======
* updated translations (en_GB)
3.21.92
=======
* update style
* updated translations (pl, vi)
3.21.91
=======
* updated translations (pl)
3.21.90
=======
* updated translations (es, gu)
3.21.4
======
* apps-menu: Fix entries from non-standard AppDir directories
3.21.3
======
* adjust to gnome-shell changes
* updated translations (oc)
3.21.2
======
* version bump, nothing to see here
3.20.1
======
* update classic style
* updated translations (gd, oc)
3.20.0
======
* version bump, nothing to see here
3.19.92
=======
* version bump, nothing to see here
3.19.91
=======
* updated translations (oc)
3.19.90
=======
* version bump, nothing to see here
3.19.4
======
* screenshot-window-sizer: HiDPI support
* Fix gnome-shell component in classic session
* updated translations (lt)
3.19.3
======
* native-window-placement: Don't let border overlap title
* apps-menu: Fix handling of .desktop files in subdirectories
* updated translations (is)
3.19.2
======
* updated translations (gd)
3.19.1
======
* Fix some theme issues
3.18.1
======
* window-list: Fix accessibility of window buttons
* apps-menu: Fix unreliable highlight
* updated translations (ar)
3.18.0
======
* Bump version
3.17.92
=======
* places: Include DESKTOP when desktop icons are enabled
* updated translations (fa)
3.17.91
=======
* updated translations (nl, pl, zh_TW)
3.17.90
=======
* window-list: Improve application ordering
* workspace-indicator: Use consistent workspace numbering
3.17.4
======
* updated translations (fur)
3.17.3
======
* window-list: Adjust with text-scaling-factor
* classic style updates
* updated translations (pt, ro)
3.17.2
======
* updated translations (oc, pt, zh_CN)
3.17.1
======
* style updates
* updated translations (oc)
3.16.1
======
* window-list: Fix workspace indicators popup menu position
* apps-menu: Fix taking over panel-main-menu shortcut
* updated translations (et, ja, lv)
3.16.0
======
* updated translations (ca)
3.15.92
=======
* classic: Update theme
* update for mutter API changes
* updated translations (bg, bs, da, fi, pa, ru, sr, sr@latin, tg)
3.15.91
=======
* classic: Update theme
* systemMonitor extension was removed, as the message tray where it
put its indicator no longer exists
* window-list: Adjust for gnome-shell changes
* updated translations (gl, it, kk, ko, lt, pl, sk, uk, zh_TW)
3.15.90
=======
* classic: Visual refresh based on new shell theme
* window-list: Adjust for gnome-shell changes
* updated translations (an, el, eo, eu, fr, he, is, sv, tr)
3.15.4
======
* window-list: Improve interaction with system modal dialogs
* updated translations (cs, de, es, eu, fur, hu, id, nb, pt_BR, ru, sl, vi)
3.15.3.1
========
* adjust to gnome-shell change
3.15.3
======
* classic-mode: Add high-contrast theme variant, drop .desktop file
* places-menu: Fix error when XDG user directories are not set up
* window-list: Add option to show on all monitors
* updated translations (eu, hu, kk, ro, tr)
3.15.2
======
* removable-drive, user-theme, window-list: Update for gnome-shell changes
* apps-menu: Fix some visual glitches
* Fix classic mode style
* updated translations (an, cs, he, vi)
3.15.1
======
* updated translations (es, nb)
3.14.1
======
* alternateTab: Fix dismissing popup with Escape
* some improvements to the window-list
(spacing in app buttons, no flash when closing windows with auto-grouping)
* updated translations (lv, it, pt, bg)
3.14.0
======
* updated translations (bn_IN, hi, kn, sr, sr@latin, uk)
3.13.92
=======
* new extension: screenshot-window-sizer
* window-list: Don't add sticky windows more than once
* updated translations (da, de, fi, ko, mr, ms, ne, pa, pl, sk, sv, ta, te, tr)
3.13.91
=======
* window-list: restore fitts'ability of workspace button
* updated for gnome-shell changes
* updated translations (cs, kk, fr, or, fa, ja, gu, id)
3.13.90
=======
* updated translations (as, ca, eu, nl, zh_CN, zh_HK, zh_TW)
3.13.4
======
* Updated for gnome-shell changes
* updated translations (el, gl, ru)
3.13.3
======
* Tweak preference UIs some more
* Fix classic mode schema overrides
* updated translations (es, he, hu, lt, nb, pt_BR, sl, tr)
3.13.2
======
* Fix sorting of grouped buttons in window list
* Tweak preference UIs
* updated translations (en_GB)
3.13.1
======
* add DesktopNames key to the classic session file
* classic theme: remove rounded corners from tile previews
* window-list: don't shift message tray on other monitors
* auto-move-windows: several fixes and updates for api changes
* launch-new-instances: updates for api changes
* updated translations (ja, km)
3.12.0
======
* updated translations (zh_HK, zh_TW)
3.11.92
=======
* nothing to see here, move on
3.11.91
=======
* updated translations (ko, fur)
3.11.90
=======
* several fixes and improvements to the window-list
(can be scrolled, works correctly with the OSD
keyboard, filters skip-taskbar windows, does not
force all notifications to bold)
* drive-menu fixed not to show shadowed mounts
* updates for gnome-shell changes (launch-new-instance,
auto-move-windows, places-menu)
* build system fixes for systems without /bin/bash
* updated translations (or, tr, uk)
3.11.5
======
* updates for gnome-shell changes
* updated translations (kn)
3.11.4
======
* classic mode now supports session saving
* updates for gnome-shell changes
* updated translations (ar, kn)
3.11.3
======
* workspace-indicator is vertically aligned now
* updated translations (ar, eo, ta, te)
3.11.2
======
* updated translations (zh_CN)
3.11.1
======
* ignore shadowed mounts in drive-menu extension
* updates for gnome-shell/gjs changes
* updated translations (el, th)
3.10.1
======
* updated translations (af, ca, ca@valencia, de, et, eu, fa,
hu, lt, lv, nb, nl, pa, pt, sk, sr, sr@latin, tr)
3.10.0
======
* updated translations (as, cs, da, es, et, fi, fr, gl,
he, id, it, kk, lt, lv, pa, pl, pt, pt_BR, ru, sl, tg,
uk, zh_HK, zh_TW)
3.9.92
======
* more updates and fixes for gnome-shell master changes
and regressions (systemMonitor, window-list, apps-menu)
* lots of updated translations (ar, as, cs, da, de, el, es,
eu, fi, gl, he, hu, it, ja, kk, ko, lv, nb, nl, pa, pl,
pt_BR, ru, sk, sr, sr@latin, ta, tg)
3.9.91
======
* update the classic mode session and theme to work with the
new system menu
* the usual round of updates and fixes for gnome-shell
API changes
* updated translations (de, it, lt, nl, pl, pt_BR, sk,
zh_HK, zh_TW)
3.9.90
======
* xrandr-indicator was removed, as the implementation
was incompatible with the new DisplayConfig mutter API
* various extensions were updated for the 3.9.90 gnome-shell API
* updated translations (cs, es, fur, gl, he, hu, id, ja, sl, tg,
zh_CN, zh_HK, zh_TW)
3.9.5
3.8.2
=====
* alternative-status-menu was removed entirely, as
it does not fit in the designs of the new unified
status menu
* updated translations (as, gu, it, ru)
3.9.4
=====
* apps-menu: fixed handling of hot corner in case
of screen reconfiguration
* alternative-status-menu now correctly honors polkit
for hibernation
* user-menu now loads themes from $XDG_DATA_HOME too
* translation updates (de, id, pt_BR, vi, zh_CN, ml)
3.9.3
=====
* classic mode mini extensions were replaced with a
GSettings override specified in the .json file
* styling of classic mode improved
* native-window-placement is back working on 3.9
* misc bug fixes
* traslation updates (an, cs, el, es, gl, nb, pl, sk, tg)
3.9.2
=====
* apps-menu: appearance of the scrollbars was improved
* window-list is a little taller in classic mode (to account
for the workspace switcher)
* alternative-status-menu honors again the dconf configuration
* translation updates (sr)
3.9.1
=====
* updates to window-list, xrandr-indicator,
workspace-indicator, windowsNavigator for gnome-shell
changes
* window-list: fix styling in classic mode
* window-list: code cleanups
* translation updates (cs, es, lt, pl, pt_BR, sl)
3.8.1

92
README Normal file
View File

@@ -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.

View File

@@ -1,76 +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
* 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.
## 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

20
autogen.sh Executable file
View File

@@ -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

3
config/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
install-sh
mkinstalldirs
missing

108
configure.ac Normal file
View File

@@ -0,0 +1,108 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell-extensions],[3.8.2],[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 default-min-max launch-new-instance static-workspaces 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|default-min-max|drive-menu|example|launch-new-instance|native-window-placement|places-menu|static-workspaces|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/default-min-max/Makefile
extensions/drive-menu/Makefile
extensions/example/Makefile
extensions/launch-new-instance/Makefile
extensions/native-window-placement/Makefile
extensions/places-menu/Makefile
extensions/static-workspaces/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

View File

@@ -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']

View File

@@ -1,2 +0,0 @@
To update the gnome-shell-sass submodule to latest upstream commit:
git submodule update --rebase

65
data/Makefile.am Normal file
View File

@@ -0,0 +1,65 @@
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)
%.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) \
$(theme_DATA) \
$(NULL)
CLEANFILES = \
$(desktop_DATA) \
$(session_DATA) \
$(xsession_DATA) \
$(mode_DATA) \
$(NULL)

View File

@@ -1,178 +0,0 @@
<?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="24"
height="24"
id="svg10621"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="calendar-today.svg">
<defs
id="defs10623">
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient99561-1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<linearGradient
inkscape:collect="always"
id="linearGradient34508-1-3">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop34510-1-9" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop34512-4-5" />
</linearGradient>
<radialGradient
r="42"
fy="30"
fx="51"
cy="30"
cx="51"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
gradientUnits="userSpaceOnUse"
id="radialGradient10592"
xlink:href="#linearGradient34508-1-3"
inkscape:collect="always" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3770"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3001"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3007"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3067"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3072"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient2997"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#d3d3d3"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="8"
inkscape:cx="-55.349829"
inkscape:cy="-31.442864"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="2133"
inkscape:window-height="1241"
inkscape:window-x="238"
inkscape:window-y="89"
inkscape:window-maximized="0"
borderlayer="true"
inkscape:showpageshadow="false">
<inkscape:grid
type="xygrid"
id="grid3109"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata10626">
<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(-469.08263,-537.99307)">
<circle
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#555753;fill-opacity:0.23756906;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="path7305"
cx="481.57138"
cy="559.4649"
r="1.5" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -14,10 +14,20 @@
height="22"
id="svg10865"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="classic-toggle-off-intl.svg">
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
@@ -67,6 +77,16 @@
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
@@ -88,76 +108,6 @@
id="linearGradient11647"
xlink:href="#linearGradient62821-5-8-1"
inkscape:collect="always" />
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath65663">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#729fcf;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:new"
id="rect65665"
width="96.999924"
height="24.292892"
x="708.71954"
y="406.96973"
rx="3.1139846"
ry="1.9595497"
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/content selection/content-selection.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
</clipPath>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9"
id="linearGradient65582"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.5250827,0,0,0.72144839,620.16092,156.4917)"
x1="207.17195"
y1="497.39584"
x2="207.17195"
y2="531.48669" />
<linearGradient
inkscape:collect="always"
id="linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9">
<stop
style="stop-color:#f4f6f4;stop-opacity:1"
offset="0"
id="stop5380-6-1-1-9-3-6-3-6-45-6-4-4-9-9-8-9" />
<stop
style="stop-color:#d7dad7;stop-opacity:1"
offset="1"
id="stop5382-06-3-6-4-2-4-6-8-0-9-6-8-7-7-6-7" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient65584">
<stop
style="stop-color:#f1f1f1;stop-opacity:1"
offset="0"
id="stop65586" />
<stop
style="stop-color:#a7aba7;stop-opacity:0;"
offset="1"
id="stop65588" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9"
id="linearGradient65582-5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.5250827,0,0,0.72144839,-75.348673,-359.16458)"
x1="207.17195"
y1="497.39584"
x2="207.17195"
y2="531.48669" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient65584"
id="linearGradient65590-6"
x1="716.62506"
y1="537.23358"
x2="716.62506"
y2="535.23358"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0139469,0,0,1.0139469,-705.52354,-521.99915)" />
</defs>
<sodipodi:namedview
id="base"
@@ -167,8 +117,8 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="23.565368"
inkscape:cy="19.596892"
inkscape:cx="-133.20351"
inkscape:cy="20.84411"
inkscape:document-units="px"
inkscape:current-layer="g62929"
showgrid="false"
@@ -179,8 +129,8 @@
inkscape:bbox-paths="true"
inkscape:snap-others="false"
inkscape:window-width="2560"
inkscape:window-height="1342"
inkscape:window-x="0"
inkscape:window-height="1380"
inkscape:window-x="1600"
inkscape:window-y="28"
inkscape:window-maximized="1"
showborder="false">
@@ -196,7 +146,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@@ -216,7 +166,7 @@
transform="translate(0,30)"
id="g62931">
<rect
style="fill:#cecece;fill-opacity:1;stroke:#a7a7a7;stroke-width:1;stroke-linecap:butt;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:new;clip-rule:nonzero;opacity:1;isolation:auto;mix-blend-mode:normal;solid-color:#000000;solid-opacity:1;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto"
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"
@@ -225,7 +175,7 @@
rx="2"
ry="2" />
<rect
style="fill:url(#linearGradient65582);fill-opacity:1;stroke:#a7a7a7;stroke-width:0.97313344;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;color:#000000;clip-rule:nonzero;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill-rule:nonzero;stroke-linejoin:miter;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
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"
@@ -235,18 +185,56 @@
ry="1" />
<g
transform="translate(-38.048674,-1.9445437)"
id="g62939" />
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.78241,486.57192 27.73523,0"
inkscape:original-d="m 647.78624,486.57192 27.94632,0"
inkscape:path-effect="#path-effect62989-8-0"
id="path62947"
d="m 647.78241,486.57192 27.73523,0"
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"
@@ -254,9 +242,9 @@
sodipodi:cy="11.875"
sodipodi:rx="4.625"
sodipodi:ry="4.625"
d="M 55,11.875 A 4.625,4.625 0 0 1 50.375,16.5 4.625,4.625 0 0 1 45.75,11.875 4.625,4.625 0 0 1 50.375,7.25 4.625,4.625 0 0 1 55,11.875 Z"
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:#2e3436;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
style="fill:none;stroke:#555753;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -14,10 +14,20 @@
height="22"
id="svg10865"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="classic-toggle-off-us.svg">
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
@@ -67,6 +77,16 @@
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
@@ -88,32 +108,6 @@
id="linearGradient11647"
xlink:href="#linearGradient62821-5-8-1"
inkscape:collect="always" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9"
id="linearGradient65582"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.5250827,0,0,0.72144839,620.16092,156.4917)"
x1="207.17195"
y1="497.39584"
x2="207.17195"
y2="531.48669" />
<linearGradient
inkscape:collect="always"
id="linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9">
<stop
style="stop-color:#f4f6f4;stop-opacity:1"
offset="0"
id="stop5380-6-1-1-9-3-6-3-6-45-6-4-4-9-9-8-9" />
<stop
style="stop-color:#d7dad7;stop-opacity:1"
offset="1"
id="stop5382-06-3-6-4-2-4-6-8-0-9-6-8-7-7-6-7" />
</linearGradient>
<inkscape:path-effect
is_visible="true"
id="path-effect62989-8-0-6"
effect="spiro" />
</defs>
<sodipodi:namedview
id="base"
@@ -123,8 +117,8 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="78.222729"
inkscape:cy="-3.6620078"
inkscape:cx="50.925774"
inkscape:cy="11.296867"
inkscape:document-units="px"
inkscape:current-layer="g62929"
showgrid="false"
@@ -135,8 +129,8 @@
inkscape:bbox-paths="true"
inkscape:snap-others="false"
inkscape:window-width="2560"
inkscape:window-height="1342"
inkscape:window-x="0"
inkscape:window-height="1375"
inkscape:window-x="1600"
inkscape:window-y="28"
inkscape:window-maximized="1"
showborder="false">
@@ -152,7 +146,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@@ -169,11 +163,10 @@
id="g62929"
transform="translate(-643.91421,517.29894)">
<g
style="display:inline"
transform="translate(0,30)"
id="g62931">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#cecece;fill-opacity:1;fill-rule:nonzero;stroke:#a7a7a7;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new"
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"
@@ -182,7 +175,7 @@
rx="2"
ry="2" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient65582);fill-opacity:1;fill-rule:nonzero;stroke:#a7a7a7;stroke-width:0.97313344;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
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"
@@ -192,18 +185,58 @@
ry="1" />
<g
transform="translate(-38.048674,-1.9445437)"
id="g62939" />
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.78241,486.57192 27.73523,0"
inkscape:path-effect="#path-effect62989-8-0-6"
inkscape:original-d="m 647.78624,486.57192 27.94632,0"
inkscape:path-effect="#path-effect62989-8-0"
id="path62947"
d="m 647.78241,486.57192 27.73523,0"
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"

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@@ -14,10 +14,42 @@
height="22"
id="svg10865"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="classic-toggle-on-intl.svg">
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
@@ -37,41 +69,6 @@
is_visible="true"
id="path-effect62989-8-0"
effect="spiro" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient38513"
id="linearGradient38519"
x1="690"
y1="506.25049"
x2="690"
y2="480.93414"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient38513">
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="0"
id="stop38515" />
<stop
style="stop-color:#6f9ccd;stop-opacity:1"
offset="1"
id="stop38517" />
</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" />
<inkscape:path-effect
is_visible="true"
id="path-effect62989-8-0-8"
effect="spiro" />
</defs>
<sodipodi:namedview
id="base"
@@ -81,8 +78,8 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="9.3225163"
inkscape:cy="-0.1588306"
inkscape:cx="14.199007"
inkscape:cy="11.127758"
inkscape:document-units="px"
inkscape:current-layer="g62929"
showgrid="false"
@@ -93,8 +90,8 @@
inkscape:bbox-paths="true"
inkscape:snap-others="false"
inkscape:window-width="2560"
inkscape:window-height="1342"
inkscape:window-x="0"
inkscape:window-height="1380"
inkscape:window-x="1600"
inkscape:window-y="28"
inkscape:window-maximized="1"
showborder="false">
@@ -110,7 +107,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@@ -127,11 +124,10 @@
id="g62929"
transform="translate(-643.91421,517.29894)">
<g
style="display:inline"
transform="translate(0,30)"
id="g62931">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient38519);fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
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"
@@ -140,7 +136,7 @@
rx="2"
ry="2" />
<rect
style="display:inline;fill:url(#linearGradient62981-1-1);fill-opacity:1;stroke:#3465a4;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
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"
@@ -150,11 +146,39 @@
ry="1" />
<g
transform="translate(-7.9861743,-1.9445437)"
id="g62939" />
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-8"
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" />
@@ -163,7 +187,7 @@
inkscape:connector-curvature="0"
id="path4444"
d="m 661.91421,522.09451 0,7"
style="fill:#3465a4;stroke:#3465a4;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" />
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"

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -14,10 +14,32 @@
height="22"
id="svg10865"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="classic-toggle-on-us.svg">
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"
@@ -47,53 +69,6 @@
is_visible="true"
id="path-effect62989-8-0"
effect="spiro" />
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath65663">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#729fcf;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:new"
id="rect65665"
width="96.999924"
height="24.292892"
x="708.71954"
y="406.96973"
rx="3.1139846"
ry="1.9595497"
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/content selection/content-selection.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
</clipPath>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient38513"
id="linearGradient38519"
x1="690"
y1="506.25049"
x2="690"
y2="480.93414"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient38513">
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="0"
id="stop38515" />
<stop
style="stop-color:#6f9ccd;stop-opacity:1"
offset="1"
id="stop38517" />
</linearGradient>
<linearGradient
gradientTransform="translate(-644,-483)"
inkscape:collect="always"
xlink:href="#linearGradient38513"
id="linearGradient38519-3"
x1="690"
y1="506.25049"
x2="690"
y2="480.93414"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
@@ -103,8 +78,8 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="20.775745"
inkscape:cy="7.0434022"
inkscape:cx="8.2018252"
inkscape:cy="8.0650495"
inkscape:document-units="px"
inkscape:current-layer="g62929"
showgrid="false"
@@ -115,8 +90,8 @@
inkscape:bbox-paths="true"
inkscape:snap-others="false"
inkscape:window-width="2560"
inkscape:window-height="1342"
inkscape:window-x="0"
inkscape:window-height="1375"
inkscape:window-x="1600"
inkscape:window-y="28"
inkscape:window-maximized="1"
showborder="false">
@@ -152,7 +127,7 @@
transform="translate(0,30)"
id="g62931">
<rect
style="fill:url(#linearGradient38519);fill-opacity:1;stroke:#3465a4;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill-rule:nonzero;stroke-linejoin:miter;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
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"
@@ -161,7 +136,7 @@
rx="2"
ry="2" />
<rect
style="fill:url(#linearGradient62981-1-1);fill-opacity:1;stroke:#3465a4;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
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"
@@ -171,7 +146,35 @@
ry="1" />
<g
transform="translate(-7.9861743,-1.9445437)"
id="g62939" />
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"
@@ -186,7 +189,7 @@
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:#3465a4;fill-opacity:1;stroke:none;display:inline;font-family:Cantarell;-inkscape-font-specification:Cantarell Bold;"
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"

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -4,6 +4,7 @@
"enabledExtensions": [@CLASSIC_EXTENSIONS@],
"panel": { "left": ["activities", "appMenu"],
"center": [],
"right": ["a11y", "keyboard", "dateMenu", "aggregateMenu"]
"right": ["a11y", "keyboard", "volume", "bluetooth",
"network", "battery", "dateMenu", "userMenu"]
}
}

View File

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

229
data/gnome-classic.css Normal file
View File

@@ -0,0 +1,229 @@
@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-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);
}
/* 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;
}

View File

@@ -1,7 +1,7 @@
[Desktop Entry]
Name=GNOME Classic
Comment=This session logs you into GNOME Classic
Exec=env GNOME_SHELL_SESSION_MODE=classic gnome-session --session gnome-classic
_Name=GNOME Classic
_Comment=This session logs you into GNOME Classic
Exec=gnome-session --session gnome-classic
TryExec=gnome-session
Icon=
Type=Application
DesktopNames=GNOME-Classic;GNOME;

View File

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

View File

@@ -1,3 +0,0 @@
[GNOME Session]
Name=GNOME Classic
RequiredComponents=org.gnome.Shell;org.gnome.SettingsDaemon.A11ySettings;org.gnome.SettingsDaemon.Clipboard;org.gnome.SettingsDaemon.Color;org.gnome.SettingsDaemon.Datetime;org.gnome.SettingsDaemon.Housekeeping;org.gnome.SettingsDaemon.Keyboard;org.gnome.SettingsDaemon.MediaKeys;org.gnome.SettingsDaemon.Mouse;org.gnome.SettingsDaemon.Power;org.gnome.SettingsDaemon.PrintNotifications;org.gnome.SettingsDaemon.Rfkill;org.gnome.SettingsDaemon.ScreensaverProxy;org.gnome.SettingsDaemon.Sharing;org.gnome.SettingsDaemon.Smartcard;org.gnome.SettingsDaemon.Sound;org.gnome.SettingsDaemon.Wacom;org.gnome.SettingsDaemon.XSettings;

View File

@@ -0,0 +1,4 @@
[GNOME Session]
_Name=GNOME Classic
RequiredComponents=gnome-shell-classic;gnome-settings-daemon;nautilus-classic;
IsRunnableHelper=@libexecdir@/gnome-session-check-accelerated

View File

@@ -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

View File

@@ -1,63 +0,0 @@
sessions = [
['gnome-classic.session.desktop.in', sessiondir],
['gnome-classic.desktop.in', xsessiondir]
]
foreach s : sessions
name_array = s[0].split('.')
i18n.merge_file('',
input: s[0],
output: '.'.join([name_array[0], name_array[1]]),
po_dir: '../po',
install: true,
install_dir: s[1],
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
)
theme_sources = files(
'gnome-shell-sass/_colors.scss',
'gnome-shell-sass/_common.scss',
'gnome-shell-sass/_drawing.scss',
'gnome-shell-sass/_high-contrast-colors.scss'
)
theme_data = [
'calendar-today.svg',
'classic-process-working.svg',
'classic-toggle-off-intl.svg',
'classic-toggle-off-us.svg',
'classic-toggle-on-intl.svg',
'classic-toggle-on-us.svg',
'gnome-classic-high-contrast.css'
]
style = 'gnome-classic'
custom_target(style + '.css',
input: style + '.scss',
output: style + '.css',
depend_files: theme_sources,
command: [sassc, '-a', '@INPUT@', '@OUTPUT@'],
install: true,
install_dir: themedir
)
install_data(theme_data, install_dir: themedir)
classic_override = '00_org.gnome.shell.extensions.classic.gschema.override'
install_data(classic_override, install_dir: schemadir)

View File

@@ -1,37 +0,0 @@
#!/bin/sh
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
ninja -C$builddir install
rm -rf $srcdir/zip-files
mkdir $srcdir/zip-files
extensiondir=$installdir/share/gnome-shell/extensions
schemadir=$installdir/share/glib-2.0/schemas
localedir=$installdir/share/locale
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
cp $srcdir/NEWS $srcdir/COPYING $f
cp -r $localedir $f
if [ -f $schema ]; then
mkdir $f/schemas
cp $schema $f/schemas;
glib-compile-schemas $f/schemas
fi
(cd $f && zip -rmq $srcdir/zip-files/$uuid.shell-extension.zip .)
done
rm -rf $builddir
rm -rf $installdir

18
extension.mk Normal file
View File

@@ -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

3
extensions/Makefile.am Normal file
View File

@@ -0,0 +1,3 @@
DIST_SUBDIRS = $(ALL_EXTENSIONS)
SUBDIRS = $(ENABLED_EXTENSIONS)

View File

@@ -0,0 +1,5 @@
EXTENSION_ID = alternate-tab
EXTRA_MODULES = prefs.js
include ../../extension.mk

View File

@@ -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];
}

View File

@@ -1,7 +0,0 @@
extension_data += configure_file(
input: metadata_name + '.in',
output: metadata_name,
configuration: metadata_conf
)
extension_sources += files('prefs.js')

View File

@@ -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@"
}

View File

@@ -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;
}

View File

@@ -0,0 +1 @@
/* This extensions requires no special styling */

View File

@@ -0,0 +1,4 @@
EXTENSION_ID = alternative-status-menu
include ../../extension.mk
include ../../settings.mk

View File

@@ -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.hibernateItem.actor.visible = this._haveHibernate;
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());
}

View File

@@ -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@"
}

View File

@@ -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>

View File

@@ -0,0 +1,3 @@
.popup-alternating-menu-item:alternate {
font-weight: normal !important;
}

View File

@@ -0,0 +1,3 @@
EXTENSION_ID = apps-menu
include ../../extension.mk

View File

@@ -1,20 +1,18 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Atk = imports.gi.Atk;
const DND = imports.ui.dnd;
const GMenu = imports.gi.GMenu;
const GObject = imports.gi.GObject;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Clutter = imports.gi.Clutter;
const Main = imports.ui.main;
const Meta = imports.gi.Meta;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Gtk = imports.gi.Gtk;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Signals = imports.signals;
const Layout = imports.ui.layout;
const Pango = imports.gi.Pango;
const Gettext = imports.gettext.domain('gnome-shell-extensions');
@@ -31,94 +29,78 @@ const HORIZ_FACTOR = 5;
const MENU_HEIGHT_OFFSET = 132;
const NAVIGATION_REGION_OVERSHOOT = 50;
class ActivitiesMenuItem extends PopupMenu.PopupBaseMenuItem {
constructor(button) {
super();
this._button = button;
this.actor.add_child(new St.Label({ text: _("Activities Overview") }));
}
const ActivitiesMenuItem = new Lang.Class({
Name: 'ActivitiesMenuItem',
Extends: PopupMenu.PopupBaseMenuItem,
activate(event) {
_init: function(button) {
this.parent();
this._button = button;
this.addActor(new St.Label({ text: _("Activities Overview") }));
},
activate: function(event) {
this._button.menu.toggle();
Main.overview.toggle();
super.activate(event);
}
};
this.parent(event);
},
});
class ApplicationMenuItem extends PopupMenu.PopupBaseMenuItem {
constructor(button, app) {
super();
this._app = app;
const ApplicationMenuItem = new Lang.Class({
Name: 'ApplicationMenuItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function(button, app) {
this.parent();
this._app = app;
this._button = button;
this._iconBin = new St.Bin();
this.actor.add_child(this._iconBin);
this.addActor(this._iconBin);
let appLabel = new St.Label({ text: app.get_name(), y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
this.actor.add_child(appLabel);
let appLabel = new St.Label({ text: app.get_name() });
this.addActor(appLabel, { span: -1, expand: true });
this.actor.label_actor = appLabel;
let textureCache = St.TextureCache.get_default();
let iconThemeChangedId = textureCache.connect('icon-theme-changed',
this._updateIcon.bind(this));
this.actor.connect('destroy', () => {
textureCache.disconnect(iconThemeChangedId);
});
Lang.bind(this, this._updateIcon));
this.actor.connect('destroy', Lang.bind(this,
function() {
textureCache.disconnect(iconThemeChangedId);
}));
this._updateIcon();
},
this.actor._delegate = this;
let draggable = DND.makeDraggable(this.actor);
let maybeStartDrag = draggable._maybeStartDrag;
draggable._maybeStartDrag = (event) => {
if (this._dragEnabled)
return maybeStartDrag.call(draggable, event);
return false;
};
draggable.connect('drag-begin', () => {
Shell.util_set_hidden_from_pick(Main.legacyTray.actor, true);
});
draggable.connect('drag-end', () => {
Shell.util_set_hidden_from_pick(Main.legacyTray.actor, false);
});
}
activate(event) {
this._app.open_new_window(-1);
activate: function(event) {
this._app.open_new_window(event.get_time());
this._button.selectCategory(null, null);
this._button.menu.toggle();
super.activate(event);
}
this.parent(event);
},
setActive(active, params) {
setActive: function(active, params) {
if (active)
this._button.scrollToButton(this);
super.setActive(active, params);
}
this.parent(active, params);
},
setDragEnabled(enable) {
this._dragEnabled = enable;
}
_getPreferredWidth: function(actor, forHeight, alloc) {
alloc.min_size = alloc.natural_size = -1;
},
getDragActor() {
return this._app.create_icon_texture(APPLICATION_ICON_SIZE);
_updateIcon: function() {
this._iconBin.set_child(this._app.create_icon_texture(APPLICATION_ICON_SIZE));
}
});
getDragActorSource() {
return this._iconBin;
}
const CategoryMenuItem = new Lang.Class({
Name: 'CategoryMenuItem',
Extends: PopupMenu.PopupBaseMenuItem,
_updateIcon() {
this._iconBin.set_child(this.getDragActor());
}
};
class CategoryMenuItem extends PopupMenu.PopupBaseMenuItem {
constructor(button, category) {
super();
this._category = category;
_init: function(button, category) {
this.parent();
this._category = category;
this._button = button;
this._oldX = -1;
@@ -130,17 +112,17 @@ class CategoryMenuItem extends PopupMenu.PopupBaseMenuItem {
else
name = _("Favorites");
this.actor.add_child(new St.Label({ text: name }));
this.actor.connect('motion-event', this._onMotionEvent.bind(this));
}
this.addActor(new St.Label({ text: name }));
this.actor.connect('motion-event', Lang.bind(this, this._onMotionEvent));
},
activate(event) {
activate: function(event) {
this._button.selectCategory(this._category, this);
this._button.scrollToCatButton(this);
super.activate(event);
}
this.parent(event);
},
_isNavigatingSubmenu([x, y]) {
_isNavigatingSubmenu: function([x, y]) {
let [posX, posY] = this.actor.get_transformed_position();
if (this._oldX == -1) {
@@ -195,9 +177,9 @@ class CategoryMenuItem extends PopupMenu.PopupBaseMenuItem {
return true;
return false;
}
},
_onMotionEvent(actor, event) {
_onMotionEvent: function(actor, event) {
if (!Clutter.get_pointer_grab()) {
this._oldX = -1;
this._oldY = -1;
@@ -212,300 +194,138 @@ class CategoryMenuItem extends PopupMenu.PopupBaseMenuItem {
this._oldY = -1;
this.actor.hover = false;
Clutter.ungrab_pointer();
let source = event.get_source();
if (source instanceof St.Widget)
source.sync_hover();
return false;
}
},
setActive(active, params) {
setActive: function(active, params) {
if (active) {
this._button.selectCategory(this._category, this);
this._button.scrollToCatButton(this);
}
super.setActive(active, params);
this.parent(active, params);
}
};
});
class ApplicationsMenu extends PopupMenu.PopupMenu {
constructor(sourceActor, arrowAlignment, arrowSide, button) {
super(sourceActor, arrowAlignment, arrowSide);
this._button = button;
}
const HotCorner = new Lang.Class({
Name: 'HotCorner',
Extends: Layout.HotCorner,
isEmpty() {
_onCornerEntered : function() {
if (!this._entered) {
this._entered = true;
if (!Main.overview.animationInProgress) {
this._activationTime = Date.now() / 1000;
this.rippleAnimation();
Main.overview.toggle();
}
}
return false;
}
});
open(animate) {
this._button.hotCorner.setBarrierSize(0);
if (this._button.hotCorner.actor) // fallback corner
this._button.hotCorner.actor.hide();
super.open(animate);
}
const ApplicationsMenu = new Lang.Class({
Name: 'ApplicationsMenu',
Extends: PopupMenu.PopupMenu,
close(animate) {
_init: function(sourceActor, arrowAlignment, arrowSide, button, hotCorner) {
this.parent(sourceActor, arrowAlignment, arrowSide);
this._button = button;
this._hotCorner = hotCorner;
},
open: function(animate) {
this._hotCorner.setBarrierSize(0);
if (this._hotCorner.actor) // fallback corner
this._hotCorner.actor.hide();
this.parent(animate);
},
close: function(animate) {
let size = Main.layoutManager.panelBox.height;
this._button.hotCorner.setBarrierSize(size);
if (this._button.hotCorner.actor) // fallback corner
this._button.hotCorner.actor.show();
super.close(animate);
}
this._hotCorner.setBarrierSize(size);
if (this._hotCorner.actor) // fallback corner
this._hotCorner.actor.show();
this.parent(animate);
},
toggle() {
toggle: function() {
if (this.isOpen) {
this._button.selectCategory(null, null);
} else {
if (Main.overview.visible)
Main.overview.hide();
}
super.toggle();
this.parent();
}
};
});
class DesktopTarget {
constructor() {
this._desktop = null;
this._desktopDestroyedId = 0;
const ApplicationsButton = new Lang.Class({
Name: 'ApplicationsButton',
Extends: PanelMenu.Button,
this._windowAddedId =
global.window_group.connect('actor-added',
this._onWindowAdded.bind(this));
_init: function() {
this.parent(1.0, null, false);
this._hotCorner = Main.layoutManager.hotCorners[Main.layoutManager.primaryIndex];
global.get_window_actors().forEach(a => {
this._onWindowAdded(a.get_parent(), a);
});
}
get hasDesktop() {
return this._desktop != null;
}
_onWindowAdded(group, actor) {
if (!(actor instanceof Meta.WindowActor))
return;
if (actor.meta_window.get_window_type() == Meta.WindowType.DESKTOP)
this._setDesktop(actor);
}
_setDesktop(desktop) {
if (this._desktop) {
this._desktop.disconnect(this._desktopDestroyedId);
this._desktopDestroyedId = 0;
delete this._desktop._delegate;
}
this._desktop = desktop;
this.emit('desktop-changed');
if (this._desktop) {
this._desktopDestroyedId = this._desktop.connect('destroy', () => {
this._setDesktop(null);
});
this._desktop._delegate = this;
}
}
_getSourceAppInfo(source) {
if (!(source instanceof ApplicationMenuItem))
return null;
return source._app.app_info;
}
_touchFile(file) {
let queryFlags = Gio.FileQueryInfoFlags.NONE;
let ioPriority = GLib.PRIORITY_DEFAULT;
let info = new Gio.FileInfo();
info.set_attribute_uint64(Gio.FILE_ATTRIBUTE_TIME_ACCESS,
GLib.get_real_time());
file.set_attributes_async (info, queryFlags, ioPriority, null,
(o, res) => {
try {
o.set_attributes_finish(res);
} catch(e) {
log('Failed to update access time: ' + e.message);
}
});
}
_markTrusted(file) {
let modeAttr = Gio.FILE_ATTRIBUTE_UNIX_MODE;
let trustedAttr = 'metadata::trusted';
let queryFlags = Gio.FileQueryInfoFlags.NONE;
let ioPriority = GLib.PRIORITY_DEFAULT;
file.query_info_async(modeAttr, queryFlags, ioPriority, null,
(o, res) => {
try {
let info = o.query_info_finish(res);
let mode = info.get_attribute_uint32(modeAttr) | 0o100;
info.set_attribute_uint32(modeAttr, mode);
info.set_attribute_string(trustedAttr, 'yes');
file.set_attributes_async (info, queryFlags, ioPriority, null,
(o, res) => {
o.set_attributes_finish(res);
// Hack: force nautilus to reload file info
this._touchFile(file);
});
} catch(e) {
log('Failed to mark file as trusted: ' + e.message);
}
});
}
destroy() {
if (this._windowAddedId)
global.window_group.disconnect(this._windowAddedId);
this._windowAddedId = 0;
this._setDesktop(null);
}
handleDragOver(source, actor, x, y, time) {
let appInfo = this._getSourceAppInfo(source);
if (!appInfo)
return DND.DragMotionResult.CONTINUE;
return DND.DragMotionResult.COPY_DROP;
}
acceptDrop(source, actor, x, y, time) {
let appInfo = this._getSourceAppInfo(source);
if (!appInfo)
return false;
this.emit('app-dropped');
let desktop = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
let src = Gio.File.new_for_path(appInfo.get_filename());
let dst = Gio.File.new_for_path(GLib.build_filenamev([desktop, src.get_basename()]));
try {
// copy_async() isn't introspectable :-(
src.copy(dst, Gio.FileCopyFlags.OVERWRITE, null, null);
this._markTrusted(dst);
} catch(e) {
log('Failed to copy to desktop: ' + e.message);
}
return true;
}
};
Signals.addSignalMethods(DesktopTarget.prototype);
let ApplicationsButton = GObject.registerClass(
class ApplicationsButton extends PanelMenu.Button {
_init() {
super._init(1.0, null, false);
this.setMenu(new ApplicationsMenu(this, 1.0, St.Side.TOP, this));
this.setMenu(new ApplicationsMenu(this.actor, 1.0, St.Side.TOP, this, this._hotCorner));
Main.panel.menuManager.addMenu(this.menu);
// At this moment applications menu is not keyboard navigable at
// all (so not accessible), so it doesn't make sense to set as
// role ATK_ROLE_MENU like other elements of the panel.
this.accessible_role = Atk.Role.LABEL;
this.actor.accessible_role = Atk.Role.LABEL;
let hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
this._label = new St.Label({ text: _("Applications") });
this._label = new St.Label({ text: _("Applications"),
y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
hbox.add_child(this._label);
hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
this.actor.add_actor(this._label);
this.actor.name = 'panelApplications';
this.actor.label_actor = this._label;
this.add_actor(hbox);
this.name = 'panelApplications';
this.label_actor = this._label;
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
this.connect('captured-event', this._onCapturedEvent.bind(this));
_showingId = Main.overview.connect('showing', Lang.bind(this, function() {
this.actor.add_accessible_state (Atk.StateType.CHECKED);
}));
_hidingId = Main.overview.connect('hiding', Lang.bind(this, function() {
this.actor.remove_accessible_state (Atk.StateType.CHECKED);
}));
this._showingId = Main.overview.connect('showing', () => {
this.add_accessible_state (Atk.StateType.CHECKED);
});
this._hidingId = Main.overview.connect('hiding', () => {
this.remove_accessible_state (Atk.StateType.CHECKED);
});
Main.layoutManager.connect('startup-complete',
this._setKeybinding.bind(this));
this._setKeybinding();
this._desktopTarget = new DesktopTarget();
this._desktopTarget.connect('app-dropped', () => {
this.menu.close();
});
this._desktopTarget.connect('desktop-changed', () => {
this._applicationsButtons.forEach(item => {
item.setDragEnabled(this._desktopTarget.hasDesktop);
});
});
this._tree = new GMenu.Tree({ menu_basename: 'applications.menu' });
this._treeChangedId = this._tree.connect('changed',
this._onTreeChanged.bind(this));
this._applicationsButtons = new Map();
this.reloadFlag = false;
this._createLayout();
this._display();
this._installedChangedId = appSys.connect('installed-changed',
this._onTreeChanged.bind(this));
}
_installedChangedId = appSys.connect('installed-changed', Lang.bind(this, function() {
if (this.menu.isOpen) {
this._redisplay();
this.mainBox.show();
} else {
this.reloadFlag = true;
}
}));
_onTreeChanged() {
if (this.menu.isOpen) {
this._redisplay();
this.mainBox.show();
} else {
this.reloadFlag = true;
}
}
// Since the hot corner uses stage coordinates, Clutter won't
// queue relayouts for us when the panel moves. Queue a relayout
// when that happens.
_panelBoxChangedId = Main.layoutManager.connect('panel-box-changed', Lang.bind(this, function() {
container.queue_relayout();
}));
},
get hotCorner() {
return Main.layoutManager.hotCorners[Main.layoutManager.primaryIndex];
}
_createVertSeparator() {
_createVertSeparator: function() {
let separator = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted' });
separator.connect('repaint', this._onVertSepRepaint.bind(this));
separator.connect('repaint', Lang.bind(this, this._onVertSepRepaint));
return separator;
}
},
_onDestroy() {
Main.overview.disconnect(this._showingId);
Main.overview.disconnect(this._hidingId);
appSys.disconnect(this._installedChangedId);
this._tree.disconnect(this._treeChangedId);
this._tree = null;
Main.wm.setCustomKeybindingHandler('panel-main-menu',
Shell.ActionMode.NORMAL |
Shell.ActionMode.OVERVIEW,
Main.sessionMode.hasOverview ?
Main.overview.toggle.bind(Main.overview) :
null);
this._desktopTarget.destroy();
}
_onCapturedEvent(actor, event) {
_onCapturedEvent: function(actor, event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
if (!Main.overview.shouldToggleByCornerOrButton())
return true;
}
return false;
}
},
_onMenuKeyPress(actor, event) {
_onMenuKeyPress: function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
let direction = symbol == Clutter.KEY_Left ? Gtk.DirectionType.LEFT
@@ -513,10 +333,10 @@ class ApplicationsButton extends PanelMenu.Button {
if (this.menu.actor.navigate_focus(global.stage.key_focus, direction, false))
return true;
}
return super._onMenuKeyPress(actor, event);
}
return this.parent(actor, event);
},
_onVertSepRepaint(area) {
_onVertSepRepaint: function(area) {
let cr = area.get_context();
let themeNode = area.get_theme_node();
let [width, height] = area.get_surface_size();
@@ -529,9 +349,9 @@ class ApplicationsButton extends PanelMenu.Button {
cr.setDash([1, 3], 1); // Hard-code for now
cr.setLineWidth(stippleWidth);
cr.stroke();
}
},
_onOpenStateChanged(menu, open) {
_onOpenStateChanged: function(menu, open) {
if (open) {
if (this.reloadFlag) {
this._redisplay();
@@ -539,50 +359,35 @@ class ApplicationsButton extends PanelMenu.Button {
}
this.mainBox.show();
}
super._onOpenStateChanged(menu, open);
}
this.parent(menu, open);
},
_setKeybinding() {
Main.wm.setCustomKeybindingHandler('panel-main-menu',
Shell.ActionMode.NORMAL |
Shell.ActionMode.OVERVIEW,
() => { this.menu.toggle(); });
}
_redisplay() {
_redisplay: function() {
this.applicationsBox.destroy_all_children();
this.categoriesBox.destroy_all_children();
this._display();
}
},
_loadCategory(categoryId, dir) {
_loadCategory: function(categoryId, dir) {
let iter = dir.iter();
let nextType;
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.ENTRY) {
let entry = iter.get_entry();
let id;
try {
id = entry.get_desktop_file_id(); // catch non-UTF8 filenames
} catch(e) {
continue;
}
let app = appSys.lookup_app(id);
if (!app)
app = new Shell.App({ app_info: entry.get_app_info() });
if (app.get_app_info().should_show())
if (!entry.get_app_info().get_nodisplay()) {
let app = appSys.lookup_app_by_tree_entry(entry);
let menu_id = dir.get_menu_id();
this.applicationsByCategory[categoryId].push(app);
} else if (nextType == GMenu.TreeItemType.SEPARATOR) {
this.applicationsByCategory[categoryId].push('separator');
}
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
let subdir = iter.get_directory();
if (!subdir.get_is_nodisplay())
this._loadCategory(categoryId, subdir);
}
}
}
},
scrollToButton(button) {
scrollToButton: function(button) {
let appsScrollBoxAdj = this.applicationsScrollBox.get_vscroll_bar().get_adjustment();
let appsScrollBoxAlloc = this.applicationsScrollBox.get_allocation_box();
let currentScrollValue = appsScrollBoxAdj.get_value();
@@ -595,9 +400,9 @@ class ApplicationsButton extends PanelMenu.Button {
newScrollValue = buttonAlloc.y2 - boxHeight + 10;
if (newScrollValue != currentScrollValue)
appsScrollBoxAdj.set_value(newScrollValue);
}
},
scrollToCatButton(button) {
scrollToCatButton: function(button) {
let catsScrollBoxAdj = this.categoriesScrollBox.get_vscroll_bar().get_adjustment();
let catsScrollBoxAlloc = this.categoriesScrollBox.get_allocation_box();
let currentScrollValue = catsScrollBoxAdj.get_value();
@@ -610,31 +415,35 @@ class ApplicationsButton extends PanelMenu.Button {
newScrollValue = buttonAlloc.y2 - boxHeight + 10;
if (newScrollValue != currentScrollValue)
catsScrollBoxAdj.set_value(newScrollValue);
}
},
_createLayout() {
_createLayout: function() {
let section = new PopupMenu.PopupMenuSection();
this.menu.addMenuItem(section);
this.mainBox = new St.BoxLayout({ vertical: false });
this.leftBox = new St.BoxLayout({ vertical: true });
this.applicationsScrollBox = new St.ScrollView({ x_fill: true, y_fill: false,
y_align: St.Align.START,
style_class: 'apps-menu vfade' });
style_class: 'vfade' });
this.applicationsScrollBox.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
let vscroll = this.applicationsScrollBox.get_vscroll_bar();
vscroll.connect('scroll-start', () => {
vscroll.connect('scroll-start', Lang.bind(this, function() {
this.menu.passEvents = true;
});
vscroll.connect('scroll-stop', () => {
}));
vscroll.connect('scroll-stop', Lang.bind(this, function() {
this.menu.passEvents = false;
});
}));
this.categoriesScrollBox = new St.ScrollView({ x_fill: true, y_fill: false,
y_align: St.Align.START,
style_class: 'vfade' });
this.categoriesScrollBox.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
vscroll = this.categoriesScrollBox.get_vscroll_bar();
vscroll.connect('scroll-start', () => { this.menu.passEvents = true; });
vscroll.connect('scroll-stop', () => { this.menu.passEvents = false; });
vscroll.connect('scroll-start', Lang.bind(this, function() {
this.menu.passEvents = true;
}));
vscroll.connect('scroll-stop', Lang.bind(this, function() {
this.menu.passEvents = false;
}));
this.leftBox.add(this.categoriesScrollBox, { expand: true,
x_fill: true, y_fill: true,
y_align: St.Align.START });
@@ -647,23 +456,23 @@ class ApplicationsButton extends PanelMenu.Button {
this.applicationsBox = new St.BoxLayout({ vertical: true });
this.applicationsScrollBox.add_actor(this.applicationsBox);
this.categoriesBox = new St.BoxLayout({ vertical: true });
this.categoriesScrollBox.add_actor(this.categoriesBox);
this.categoriesScrollBox.add_actor(this.categoriesBox, { expand: true, x_fill: false });
this.mainBox.add(this.leftBox);
this.mainBox.add(this._createVertSeparator(), { expand: false, x_fill: false, y_fill: true});
this.mainBox.add(this.applicationsScrollBox, { expand: true, x_fill: true, y_fill: true });
section.actor.add_actor(this.mainBox);
}
},
_display() {
this._applicationsButtons.clear();
this.mainBox.style=('width: 35em;');
_display: function() {
this._applicationsButtons = new Array();
this.mainBox.style=('width: 640px;');
this.mainBox.hide();
//Load categories
this.applicationsByCategory = {};
this._tree.load_sync();
let root = this._tree.get_root_directory();
let tree = appSys.get_tree();
let root = tree.get_root_directory();
let categoryMenuItem = new CategoryMenuItem(this, null);
this.categoriesBox.add_actor(categoryMenuItem.actor);
let iter = root.iter();
@@ -686,48 +495,45 @@ class ApplicationsButton extends PanelMenu.Button {
//Load applications
this._displayButtons(this._listApplications(null));
let themeContext = St.ThemeContext.get_for_stage(global.stage);
let scaleFactor = themeContext.scale_factor;
let categoriesHeight = this.categoriesBox.height / scaleFactor;
let height = Math.round(categoriesHeight) + MENU_HEIGHT_OFFSET + 'px';
let height = this.categoriesBox.height + MENU_HEIGHT_OFFSET + 'px';
this.mainBox.style+=('height: ' + height);
}
},
selectCategory(dir, categoryMenuItem) {
this.applicationsBox.get_children().forEach(c => {
if (c._delegate instanceof PopupMenu.PopupSeparatorMenuItem)
c._delegate.destroy();
else
this.applicationsBox.remove_actor(c);
});
_clearApplicationsBox: function(selectedActor) {
let actors = this.applicationsBox.get_children();
for (let i = 0; i < actors.length; i++) {
let actor = actors[i];
this.applicationsBox.remove_actor(actor);
}
},
selectCategory: function(dir, categoryMenuItem) {
if (categoryMenuItem)
this._clearApplicationsBox(categoryMenuItem.actor);
else
this._clearApplicationsBox(null);
if (dir)
this._displayButtons(this._listApplications(dir.get_menu_id()));
else
this._displayButtons(this._listApplications(null));
}
},
_displayButtons(apps) {
_displayButtons: function(apps) {
if (apps) {
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
let item;
if (app instanceof Shell.App)
item = this._applicationsButtons.get(app);
else
item = new PopupMenu.PopupSeparatorMenuItem();
if (!item) {
item = new ApplicationMenuItem(this, app);
item.setDragEnabled(this._desktopTarget.hasDesktop);
this._applicationsButtons.set(app, item);
if (!this._applicationsButtons[app]) {
let applicationMenuItem = new ApplicationMenuItem(this, app);
this._applicationsButtons[app] = applicationMenuItem;
}
if (!item.actor.get_parent())
this.applicationsBox.add_actor(item.actor);
if (!this._applicationsButtons[app].actor.get_parent())
this.applicationsBox.add_actor(this._applicationsButtons[app].actor);
}
}
}
},
_listApplications(category_menu_id) {
_listApplications: function(category_menu_id) {
let applist;
if (category_menu_id) {
@@ -742,24 +548,54 @@ class ApplicationsButton extends PanelMenu.Button {
}
}
applist.sort(function(a,b) {
return a.get_name().toLowerCase() > b.get_name().toLowerCase();
});
return applist;
},
destroy: function() {
this.menu.actor.get_children().forEach(function(c) { c.destroy() });
this.parent();
}
});
let appsMenuButton;
let activitiesButton;
let _hidingId;
let _installedChangedId;
let _panelBoxChangedId;
let _showingId;
function enable() {
activitiesButton = Main.panel.statusArea['activities'];
activitiesButton.container.hide();
appsMenuButton = new ApplicationsButton();
Main.panel.addToStatusArea('apps-menu', appsMenuButton, 1, 'left');
Main.wm.setCustomKeybindingHandler('panel-main-menu',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
function() {
appsMenuButton.menu.toggle();
});
}
function disable() {
Main.panel.menuManager.removeMenu(appsMenuButton.menu);
appSys.disconnect(_installedChangedId);
Main.layoutManager.disconnect(_panelBoxChangedId);
Main.overview.disconnect(_hidingId);
Main.overview.disconnect(_showingId);
appsMenuButton.destroy();
activitiesButton.container.show();
Main.wm.setCustomKeybindingHandler('panel-main-menu',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Main.sessionMode.hasOverview ?
Lang.bind(Main.overview, Main.overview.toggle) :
null);
}
function init(metadata) {

View File

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

View File

@@ -3,7 +3,7 @@
"uuid": "@uuid@",
"gettext-domain": "@gettext_domain@",
"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" ],
"shell-version": [ "@shell_current@" ],
"url": "@url@"

View File

@@ -1,7 +1 @@
.apps-menu:ltr {
padding-right: 3px;
}
.apps-menu:rtl {
padding-left: 3px;
}
/* none used*/

View File

@@ -0,0 +1,7 @@
EXTENSION_ID = auto-move-windows
EXTRA_MODULES = prefs.js
include ../../extension.mk
include ../../settings.mk

View File

@@ -1,7 +1,13 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
// Start apps on custom workspaces
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;
const Main = imports.ui.main;
@@ -9,138 +15,153 @@ const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;
class WindowMover {
constructor() {
this._settings = Convenience.getSettings();
this._appSystem = Shell.AppSystem.get_default();
this._appConfigs = new Map();
this._appData = new Map();
const SETTINGS_KEY = 'application-list';
this._appsChangedId =
this._appSystem.connect('installed-changed',
this._updateAppData.bind(this));
let settings;
this._settings.connect('changed', this._updateAppConfigs.bind(this));
this._updateAppConfigs();
}
const WindowMover = new Lang.Class({
Name: 'AutoMoveWindows.WindowMover',
_updateAppConfigs() {
this._appConfigs.clear();
_init: function() {
this._settings = settings;
this._windowTracker = Shell.WindowTracker.get_default();
this._settings.get_strv('application-list').forEach(v => {
let [appId, num] = v.split(':');
this._appConfigs.set(appId, parseInt(num) - 1);
});
let display = global.screen.get_display();
// Connect after so the handler from ShellWindowTracker has already run
this._windowCreatedId = display.connect_after('window-created', Lang.bind(this, this._findAndMove));
},
this._updateAppData();
}
_updateAppData() {
let ids = [...this._appConfigs.keys()];
let removedApps = [...this._appData.keys()].filter(
a => !ids.includes(a.id)
);
removedApps.forEach(app => {
app.disconnect(this._appData.get(app).windowsChangedId);
this._appData.delete(app);
});
let addedApps = ids.map(id => this._appSystem.lookup_app(id)).filter(
app => app != null && !this._appData.has(app)
);
addedApps.forEach(app => {
let data = {
windowsChangedId: app.connect('windows-changed',
this._appWindowsChanged.bind(this)),
moveWindowsId: 0,
windows: app.get_windows()
}
this._appData.set(app, data);
});
}
destroy() {
if (this._appsChangedId) {
this._appSystem.disconnect(this._appsChangedId);
this._appsChangedId = 0;
destroy: function() {
if (this._windowCreatedId) {
global.screen.get_display().disconnect(this._windowCreatedId);
this._windowCreatedId = 0;
}
},
if (this._settings) {
this._settings.run_dispose();
this._settings = null;
_ensureAtLeastWorkspaces: function(num, window) {
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);
}
},
this._appConfigs.clear();
this._updateAppData();
}
_moveWindow(window, workspaceNum) {
if (window.skip_taskbar)
_findAndMove: function(display, window, noRecurse) {
if (!this._windowTracker.is_window_interesting(window))
return;
// ensure we have the required number of workspaces
let workspaceManager = global.workspace_manager;
for (let i = workspaceManager.n_workspaces; i <= workspaceNum; i++) {
window.change_workspace_by_index(i - 1, false);
workspaceManager.append_new_workspace(false, 0);
let spaces = this._settings.get_strv(SETTINGS_KEY);
let app = this._windowTracker.get_window_app(window);
if (!app) {
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);
window.change_workspace_by_index(workspace_num, false, global.get_current_time());
}
}
}
_appWindowsChanged(app) {
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 => !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;
}
};
});
let prevCheckWorkspaces;
let winMover;
function init() {
Convenience.initTranslations();
settings = Convenience.getSettings();
}
function myCheckWorkspaces() {
let keepAliveWorkspaces = [];
let foundNonEmpty = false;
for (let i = this._workspaces.length - 1; i >= 0; i--) {
if (!foundNonEmpty)
foundNonEmpty = this._workspaces[i].list_windows().length > 0;
else if (!this._workspaces[i]._keepAliveId)
keepAliveWorkspaces.push(this._workspaces[i]);
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;
}
// make sure the original method only removes empty workspaces at the end
keepAliveWorkspaces.forEach(ws => { ws._keepAliveId = 1; });
prevCheckWorkspaces.call(this);
keepAliveWorkspaces.forEach(ws => { delete ws._keepAliveId; });
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.wm._workspaceTracker._checkWorkspaces;
Main.wm._workspaceTracker._checkWorkspaces = myCheckWorkspaces;
prevCheckWorkspaces = Main._checkWorkspaces;
if (Meta.prefs_get_dynamic_workspaces())
Main._checkWorkspaces = myCheckWorkspaces;
winMover = new WindowMover();
}
function disable() {
Main.wm._workspaceTracker._checkWorkspaces = prevCheckWorkspaces;
Main._checkWorkspaces = prevCheckWorkspaces;
winMover.destroy();
}

View File

@@ -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')

View File

@@ -4,7 +4,7 @@
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"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@" ],
"original-authors": [ "alessandro.crismani@gmail.com", "thomas.bouffon@gmail.com" ],
"url": "@url@"

View File

@@ -3,8 +3,8 @@
<key name="application-list" type="as">
<!-- FIXME: should be a(su), when JS supports more of GVariant -->
<default>[ ]</default>
<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>
<_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>
</key>
</schema>
</schemalist>

View File

@@ -1,13 +1,18 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
// Start apps on custom workspaces
const GdkPixbuf = imports.gi.GdkPixbuf;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const GMenu = imports.gi.GMenu;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Gettext = imports.gettext.domain('gnome-shell-extensions');
const _ = Gettext.gettext;
const N_ = e => e;
const N_ = function(e) { return e };
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
@@ -25,225 +30,220 @@ const Columns = {
ADJUSTMENT: 4
};
const Widget = GObject.registerClass({
const Widget = new GObject.Class({
Name: 'AutoMoveWindows.Prefs.Widget',
GTypeName: 'AutoMoveWindowsPrefsWidget',
}, class Widget extends Gtk.Grid {
_init(params) {
super._init(params);
this.set_orientation(Gtk.Orientation.VERTICAL);
Extends: Gtk.Grid,
this._settings = Convenience.getSettings();
this._settings.connect('changed', this._refresh.bind(this));
this._changedPermitted = false;
_init: function(params) {
this.parent(params);
this.set_orientation(Gtk.Orientation.VERTICAL);
this._store = new Gtk.ListStore();
this._store.set_column_types([Gio.AppInfo, GObject.TYPE_STRING, Gio.Icon, GObject.TYPE_INT,
this._settings = Convenience.getSettings();
this._settings.connect('changed', Lang.bind(this, this._refresh));
this._changedPermitted = false;
this._store = new Gtk.ListStore();
this._store.set_column_types([Gio.AppInfo, GObject.TYPE_STRING, Gio.Icon, GObject.TYPE_INT,
Gtk.Adjustment]);
let scrolled = new Gtk.ScrolledWindow({ shadow_type: Gtk.ShadowType.IN});
scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this.add(scrolled);
this._treeView = new Gtk.TreeView({ model: this._store,
this._treeView = new Gtk.TreeView({ model: this._store,
hexpand: true, vexpand: true });
this._treeView.get_selection().set_mode(Gtk.SelectionMode.SINGLE);
this._treeView.get_selection().set_mode(Gtk.SelectionMode.SINGLE);
let appColumn = new Gtk.TreeViewColumn({ expand: true, sort_column_id: Columns.DISPLAY_NAME,
title: _("Application") });
let iconRenderer = new Gtk.CellRendererPixbuf;
appColumn.pack_start(iconRenderer, false);
appColumn.add_attribute(iconRenderer, "gicon", Columns.ICON);
let nameRenderer = new Gtk.CellRendererText;
appColumn.pack_start(nameRenderer, true);
appColumn.add_attribute(nameRenderer, "text", Columns.DISPLAY_NAME);
this._treeView.append_column(appColumn);
let appColumn = new Gtk.TreeViewColumn({ expand: true, sort_column_id: Columns.DISPLAY_NAME,
title: _("Application") });
let iconRenderer = new Gtk.CellRendererPixbuf;
appColumn.pack_start(iconRenderer, false);
appColumn.add_attribute(iconRenderer, "gicon", Columns.ICON);
let nameRenderer = new Gtk.CellRendererText;
appColumn.pack_start(nameRenderer, true);
appColumn.add_attribute(nameRenderer, "text", Columns.DISPLAY_NAME);
this._treeView.append_column(appColumn);
let workspaceColumn = new Gtk.TreeViewColumn({ title: _("Workspace"),
let workspaceColumn = new Gtk.TreeViewColumn({ title: _("Workspace"),
sort_column_id: Columns.WORKSPACE });
let workspaceRenderer = new Gtk.CellRendererSpin({ editable: true });
workspaceRenderer.connect('edited', this._workspaceEdited.bind(this));
workspaceColumn.pack_start(workspaceRenderer, true);
workspaceColumn.add_attribute(workspaceRenderer, "adjustment", Columns.ADJUSTMENT);
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);
this._treeView.append_column(workspaceColumn);
scrolled.add(this._treeView);
this.add(this._treeView);
let toolbar = new Gtk.Toolbar({ icon_size: Gtk.IconSize.SMALL_TOOLBAR });
toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR);
this.add(toolbar);
let toolbar = new Gtk.Toolbar();
toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR);
this.add(toolbar);
let newButton = new Gtk.ToolButton({ icon_name: 'bookmark-new-symbolic',
label: _("Add Rule"),
is_important: true });
newButton.connect('clicked', this._createNew.bind(this));
toolbar.add(newButton);
let newButton = new Gtk.ToolButton({ stock_id: Gtk.STOCK_NEW,
label: _("Add rule"),
is_important: true });
newButton.connect('clicked', Lang.bind(this, this._createNew));
toolbar.add(newButton);
let delButton = new Gtk.ToolButton({ icon_name: 'edit-delete-symbolic' });
delButton.connect('clicked', this._deleteSelected.bind(this));
toolbar.add(delButton);
let delButton = new Gtk.ToolButton({ stock_id: Gtk.STOCK_DELETE });
delButton.connect('clicked', Lang.bind(this, this._deleteSelected));
toolbar.add(delButton);
let selection = this._treeView.get_selection();
selection.connect('changed', () => {
delButton.sensitive = selection.count_selected_rows() > 0;
});
delButton.sensitive = selection.count_selected_rows() > 0;
this._changedPermitted = true;
this._refresh();
},
this._changedPermitted = true;
this._refresh();
}
_createNew() {
let dialog = new Gtk.Dialog({ title: _("Create new matching rule"),
transient_for: this.get_toplevel(),
use_header_bar: true,
modal: true });
dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL);
let addButton = dialog.add_button(_("Add"), Gtk.ResponseType.OK);
_createNew: function() {
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);
let grid = new Gtk.Grid({ column_spacing: 10,
let grid = new Gtk.Grid({ column_spacing: 10,
row_spacing: 15,
margin: 10 });
dialog._appChooser = new Gtk.AppChooserWidget({ show_all: true });
dialog._appChooser.connect('application-selected', (w, appInfo) => {
addButton.sensitive = appInfo && this._checkId(appInfo.get_id());
});
let appInfo = dialog._appChooser.get_app_info();
addButton.sensitive = appInfo && this._checkId(appInfo.get_id());
grid.attach(dialog._appChooser, 0, 0, 2, 1);
grid.attach(new Gtk.Label({ label: _("Workspace"),
halign: Gtk.Align.END }), 0, 1, 1, 1);
let adjustment = new Gtk.Adjustment({ lower: 1,
upper: WORKSPACE_MAX,
step_increment: 1
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 = 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);
grid.attach(dialog._spin, 1, 1, 1, 1);
dialog.get_content_area().add(grid);
dialog.connect('response', (dialog, id) => {
if (id != Gtk.ResponseType.OK) {
dialog.connect('response', Lang.bind(this, function(dialog, id) {
if (id != Gtk.ResponseType.OK) {
dialog.destroy();
return;
return;
}
let appInfo = dialog._appChooser.get_app_info();
if (!appInfo)
let appInfo = dialog._appChooser.get_app_info();
if (!appInfo)
return;
let index = Math.floor(dialog._spin.value);
if (isNaN(index) || index < 0)
index = 1;
this._changedPermitted = false;
if (!this._appendItem(appInfo.get_id(), index)) {
this._changedPermitted = true;
return;
let index = Math.floor(dialog._spin.value);
if (isNaN(index) || index < 0)
index = 1;
this._changedPermitted = false;
this._appendItem(appInfo.get_id(), index);
this._changedPermitted = true;
let iter = this._store.append();
let adj = new Gtk.Adjustment({ lower: 1,
upper: WORKSPACE_MAX,
step_increment: 1,
}
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._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;
dialog.destroy();
});
dialog.show_all();
}
}));
dialog.show_all();
},
_deleteSelected() {
let [any, model, iter] = this._treeView.get_selection().get_selected();
_deleteSelected: function() {
let [any, model, iter] = this._treeView.get_selection().get_selected();
if (any) {
let appInfo = this._store.get_value(iter, Columns.APPINFO);
if (any) {
let appInfo = this._store.get_value(iter, Columns.APPINFO);
this._changedPermitted = false;
this._removeItem(appInfo.get_id());
this._changedPermitted = true;
this._store.remove(iter);
}
}
this._changedPermitted = false;
this._removeItem(appInfo.get_id());
this._store.remove(iter);
this._changedPermitted = true;
}
},
_workspaceEdited(renderer, pathString, text) {
let index = parseInt(text);
if (isNaN(index) || index < 0)
index = 1;
_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);
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;
}
this._changedPermitted = false;
this._changeItem(appInfo.get_id(), index);
this._store.set_value(iter, Columns.WORKSPACE, index);
this._changedPermitted = true;
},
_refresh() {
if (!this._changedPermitted)
// Ignore this notification, model is being modified outside
return;
_refresh: function() {
if (!this._changedPermitted)
// Ignore this notification, model is being modified outside
return;
this._store.clear();
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 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,
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]);
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;
}
if (validItems.length != currentItems.length) // some items were filtered out
this._settings.set_strv(SETTINGS_KEY, validItems);
}
currentItems.push(id + ':' + workspace);
this._settings.set_strv(SETTINGS_KEY, currentItems);
return true;
},
_checkId(id) {
let items = this._settings.get_strv(SETTINGS_KEY);
return !items.some(i => i.startsWith(id + ':'));
}
_removeItem: function(id) {
let currentItems = this._settings.get_strv(SETTINGS_KEY);
let index = currentItems.map(function(el) {
return el.split(':')[0];
}).indexOf(id);
_appendItem(id, workspace) {
let currentItems = this._settings.get_strv(SETTINGS_KEY);
currentItems.push(id + ':' + workspace);
this._settings.set_strv(SETTINGS_KEY, currentItems);
}
if (index < 0)
return;
currentItems.splice(index, 1);
this._settings.set_strv(SETTINGS_KEY, currentItems);
},
_removeItem(id) {
let currentItems = this._settings.get_strv(SETTINGS_KEY);
let index = currentItems.map(el => el.split(':')[0]).indexOf(id);
_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)
return;
currentItems.splice(index, 1);
this._settings.set_strv(SETTINGS_KEY, currentItems);
}
_changeItem(id, workspace) {
let currentItems = this._settings.get_strv(SETTINGS_KEY);
let index = currentItems.map(el => el.split(':')[0]).indexOf(id);
if (index < 0)
currentItems.push(id + ':' + workspace);
else
currentItems[index] = id + ':' + workspace;
this._settings.set_strv(SETTINGS_KEY, currentItems);
if (index < 0)
currentItems.push(id + ':' + workspace);
else
currentItems[index] = id + ':' + workspace;
this._settings.set_strv(SETTINGS_KEY, currentItems);
}
});
@@ -253,7 +253,7 @@ function init() {
}
function buildPrefsWidget() {
let widget = new Widget({ margin: 12 });
let widget = new Widget();
widget.show_all();
return widget;

View File

@@ -0,0 +1,3 @@
EXTENSION_ID = default-min-max
include ../../extension.mk

View File

@@ -0,0 +1,20 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
const Meta = imports.gi.Meta;
const BUTTON_LAYOUT_KEY = 'button-layout';
const EXTENSION_SCHEMA = 'org.gnome.desktop.wm.preferences';
const SHELL_OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
function init(metadata) {
}
function enable() {
// Override gnome-shell's overrides
Meta.prefs_override_preference_schema(BUTTON_LAYOUT_KEY, EXTENSION_SCHEMA);
}
function disable() {
// Restore gnome-shell's overrides
Meta.prefs_override_preference_schema(BUTTON_LAYOUT_KEY, SHELL_OVERRIDES_SCHEMA);
}

View File

@@ -0,0 +1,11 @@
{
"extension-id": "@extension_id@",
"uuid": "@uuid@",
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"name": "Default Minimize and Maximize",
"description": "Adds minimize and maximize buttons to the titlebar by default",
"original-authors": [ "debarshir@gnome.org" ],
"shell-version": [ "@shell_current@" ],
"url": "@url@"
}

View File

@@ -0,0 +1 @@
/* This extensions requires no special styling */

View File

@@ -0,0 +1,3 @@
EXTENSION_ID = drive-menu
include ../../extension.mk

View File

@@ -1,7 +1,6 @@
// Drive menu extension
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GObject = imports.gi.GObject;
const Lang = imports.lang;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
@@ -18,42 +17,113 @@ const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;
class MountMenuItem extends PopupMenu.PopupBaseMenuItem {
constructor(mount) {
super();
const MountMenuItem = new Lang.Class({
Name: 'DriveMenu.MountMenuItem',
Extends: PopupMenu.PopupBaseMenuItem,
this.label = new St.Label({ text: mount.get_name() });
this.actor.add(this.label, { expand: true });
_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;
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', this._eject.bind(this));
this.actor.add(ejectButton);
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);
},
this._changedId = mount.connect('changed', this._syncVisibility.bind(this));
this._syncVisibility();
_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);
}
});
destroy() {
if (this._changedId) {
this.mount.disconnect(this._changedId);
this._changedId = 0;
}
const DriveMenu = new Lang.Class({
Name: 'DriveMenu.DriveMenu',
Extends: PanelMenu.SystemStatusButton,
super.destroy();
}
_init: function() {
this.parent('media-eject-symbolic', _("Removable devices"));
_isInteresting() {
if (!this.mount.can_eject() && !this.mount.can_unmount())
return false;
if (this.mount.is_shadowed())
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._mounts = [ ];
this._monitor.get_mounts().forEach(Lang.bind(this, this._addMount));
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());
});
this._updateMenuVisibility();
},
_updateMenuVisibility: function() {
if (this._mounts.length > 0)
this.actor.show();
else
this.actor.hide();
},
_isMountInteresting: function(mount) {
if (!mount.can_eject() && !mount.can_unmount())
return false;
let volume = this.mount.get_volume();
let volume = mount.get_volume();
if (volume == null) {
// probably a GDaemonMount, could be network or
@@ -62,130 +132,42 @@ class MountMenuItem extends PopupMenu.PopupBaseMenuItem {
}
return volume.get_identifier('class') != 'network';
}
},
_syncVisibility() {
this.actor.visible = this._isInteresting();
}
_addMount: function(mount) {
if (!this._isMountInteresting(mount))
return;
_eject() {
let mountOp = new ShellMountOperation.ShellMountOperation(this.mount);
let item = new MountMenuItem(mount);
this._mounts.unshift(item);
this.menu.addMenuItem(item, 0);
},
if (this.mount.can_eject())
this.mount.eject_with_operation(Gio.MountUnmountFlags.NONE,
mountOp.mountOp,
null, // Gio.Cancellable
this._ejectFinish.bind(this));
else
this.mount.unmount_with_operation(Gio.MountUnmountFlags.NONE,
mountOp.mountOp,
null, // Gio.Cancellable
this._unmountFinish.bind(this));
}
_removeMount: function(mount) {
if (!this._isMountInteresting(mount))
return;
_unmountFinish(mount, result) {
try {
mount.unmount_with_operation_finish(result);
} catch(e) {
this._reportFailure(e);
}
}
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');
},
_ejectFinish(mount, result) {
try {
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 context = global.create_app_launch_context(event.get_time(), -1);
Gio.AppInfo.launch_default_for_uri(this.mount.get_root().get_uri(),
context);
super.activate(event);
}
};
let DriveMenu = GObject.registerClass(
class DriveMenu extends PanelMenu.Button {
_init() {
super._init(0.0, _("Removable devices"));
let hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
let icon = new St.Icon({ icon_name: 'media-eject-symbolic',
style_class: 'system-status-icon' });
hbox.add_child(icon);
hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
this.add_child(hbox);
this._monitor = Gio.VolumeMonitor.get();
this._addedId = this._monitor.connect('mount-added', (monitor, mount) => {
this._addMount(mount);
this._updateMenuVisibility();
});
this._removedId = this._monitor.connect('mount-removed', (monitor, mount) => {
this._removeMount(mount);
this._updateMenuVisibility();
});
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.actor.visible).length > 0)
this.show();
else
this.hide();
}
_addMount(mount) {
let item = new MountMenuItem(mount);
this._mounts.unshift(item);
this.menu.addMenuItem(item, 0);
}
_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');
}
_onDestroy() {
if (this._addedId) {
this._monitor.disconnect(this._addedId);
this._monitor.disconnect(this._removedId);
this._addedId = 0;
this._removedId = 0;
destroy: function() {
if (this._connectedId) {
this._monitor.disconnect(this._connectedId);
this._monitor.disconnect(this._disconnectedId);
this._connectedId = 0;
this._disconnectedId = 0;
}
super._onDestroy();
}
this.parent();
},
});
function init() {

View File

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

View File

@@ -4,7 +4,7 @@
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"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@" ],
"url": "@url@"
}

View File

@@ -0,0 +1,6 @@
EXTENSION_ID = example
EXTRA_MODULES = prefs.js
include ../../extension.mk
include ../../settings.mk

View File

@@ -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;
}
}

View File

@@ -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@"
}

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -1,8 +1,8 @@
.screenshot-sizer-message {
/* Example stylesheet */
.helloworld-label {
font-size: 36px;
font-weight: bold;
color: #ffffff;
background-color: rgba(10,10,10,0.7);
border-radius: 5px;
padding: .5em;
}

View File

@@ -0,0 +1,3 @@
EXTENSION_ID = launch-new-instance
include ../../extension.mk

View File

@@ -1,17 +1,37 @@
const Main = imports.ui.main;
const AppDisplay = imports.ui.appDisplay;
let _activateOriginal = null;
var _onActivateOriginal = null;
var _activateResultOriginal = null;
function _onActivate(event) {
this.emit('launching');
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() {
_activateOriginal = AppDisplay.AppIcon.prototype.activate;
AppDisplay.AppIcon.prototype.activate = function() {
_activateOriginal.call(this, 2);
};
_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.activate = _activateOriginal;
AppDisplay.AppIcon.prototype._onActivate = _onActivateOriginal;
AppDisplay.AppSearchProvider.prototype.activateResult = _activateResultOriginal;
}

View File

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

View File

@@ -4,7 +4,7 @@
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"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@" ],
"url": "@url@"
}

View File

@@ -1,45 +0,0 @@
extension_schemas = []
js_sources = extensionlib
metadata_name = 'metadata.json'
foreach e : all_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 = files(e + '/stylesheet.css')
subdir(e)
js_sources += extension_sources
if (enabled_extensions.contains(e))
install_data (extension_sources + extension_data + extensionlib,
install_dir: join_paths(extensiondir, uuid)
)
endif
endforeach
install_data (extension_schemas,
install_dir: schemadir
)
foreach js_source : js_sources
if (js52.found())
path_array = '@0@'.format(js_source).split('/')
name = join_paths(path_array[-2], path_array[-1])
test('Checking syntax of ' + name, js52,
args: ['-s', '-c', js_source],
workdir: meson.current_source_dir()
)
endif
endforeach

View File

@@ -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')

View File

@@ -0,0 +1,5 @@
EXTENSION_ID = native-window-placement
include ../../extension.mk
include ../../settings.mk

View File

@@ -1,5 +1,24 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
// 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;
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;
const Workspace = imports.ui.workspace;
const WindowPositionFlags = Workspace.WindowPositionFlags;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
@@ -12,19 +31,40 @@ const WINDOW_PLACEMENT_NATURAL_ACCURACY = 20; // accuracy
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
class Rect {
constructor(x, y, width, height) {
[this.x, this.y, this.width, this.height] = [x, y, width, height];
const PLACE_WINDOW_CAPTIONS_ON_TOP = true; // place window titles in overview on top of windows with overlap parameter
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];
},
/**
* used in _calculateWindowTransformationsNatural to replace Meta.Rectangle that is too slow.
*/
copy() {
copy: function() {
return new Rect(this.x, this.y, this.width, this.height);
}
},
union(rect2) {
union: function(rect2) {
let dest = this.copy();
if (rect2.x < dest.x)
{
@@ -42,64 +82,113 @@ class Rect {
dest.height = rect2.y + rect2.height - dest.y;
return dest;
}
},
adjusted(dx, dy, dx2, dy2) {
adjusted: function(dx, dy, dx2, dy2) {
let dest = this.copy();
dest.x += dx;
dest.y += dy;
dest.width += -dx + dx2;
dest.height += -dy + dy2;
return dest;
}
},
overlap(rect2) {
overlap: function(rect2) {
return !((this.x + this.width <= rect2.x) ||
(rect2.x + rect2.width <= this.x) ||
(this.y + this.height <= rect2.y) ||
(rect2.y + rect2.height <= this.y));
}
},
center() {
center: function() {
return [this.x + this.width / 2, this.y + this.height / 2];
}
},
translate(dx, dy) {
translate: function(dx, dy) {
this.x += dx;
this.y += dy;
}
};
});
class NaturalLayoutStrategy extends Workspace.LayoutStrategy {
constructor(settings) {
super();
this._settings = settings;
}
let winInjections, workspaceInjections, connectedSignals;
computeLayout(windows, layout) {
layout.windows = windows;
}
function resetState() {
winInjections = { };
workspaceInjections = { };
connectedSignals = [ ];
}
function enable() {
resetState();
let settings = Convenience.getSettings();
let useMoreScreen = settings.get_boolean('use-more-screen');
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.
* This function is almost a 1:1 copy of the function
* PresentWindowsEffect::calculateWindowTransformationsNatural() from KDE, see:
* 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
// is always sorted the same way no matter which window is currently active.
clones = clones.sort(function (win1, win2) {
return win2.metaWindow.get_stable_sequence() - win1.metaWindow.get_stable_sequence();
});
let area_rect = new Rect(area.x, area.y, area.width, area.height);
let bounds = area_rect.copy();
let clones = layout.windows;
let node = this.actor.get_theme_node();
let columnSpacing = node.get_length('-horizontal-spacing');
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 directions = [];
let rects = [];
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]
let rect = clones[i].metaWindow.get_frame_rect();
let rect = clones[i].metaWindow.get_outer_rect();
rects[i] = new Rect(rect.x, rect.y, rect.width, rect.height);
bounds = bounds.union(rects[i]);
@@ -135,7 +224,7 @@ class NaturalLayoutStrategy extends Workspace.LayoutStrategy {
if (diff[0] == 0 && diff[1] == 0)
diff[0] = 1;
// Try to keep screen/workspace aspect ratio
if ( bounds.height / bounds.width > area_rect.height / area_rect.width )
if ( bounds.height / bounds.width > area.height / area.width )
diff[0] *= 2;
else
diff[1] *= 2;
@@ -150,7 +239,7 @@ class NaturalLayoutStrategy extends Workspace.LayoutStrategy {
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
// 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
@@ -207,15 +296,15 @@ class NaturalLayoutStrategy extends Workspace.LayoutStrategy {
// Work out scaling by getting the most top-left and most bottom-right window coords.
let scale;
scale = Math.min(area_rect.width / bounds.width,
area_rect.height / bounds.height,
scale = Math.min(area.width / bounds.width,
area.height / bounds.height,
1.0);
// Make bounding rect fill the screen size for later steps
bounds.x = bounds.x - (area_rect.width - bounds.width * scale) / 2;
bounds.y = bounds.y - (area_rect.height - bounds.height * scale) / 2;
bounds.width = area_rect.width / scale;
bounds.height = area_rect.height / scale;
bounds.x = bounds.x - (area.width - bounds.width * scale) / 2;
bounds.y = bounds.y - (area.height - bounds.height * scale) / 2;
bounds.width = area.width / scale;
bounds.height = area.height / scale;
// Move all windows back onto the screen and set their scale
for (let i = 0; i < rects.length; i++) {
@@ -230,49 +319,147 @@ class NaturalLayoutStrategy extends Workspace.LayoutStrategy {
let buttonOuterHeight, captionHeight;
let buttonOuterWidth = 0;
let slots = [];
let targets = [];
for (let i = 0; i < rects.length; i++) {
rects[i].x = rects[i].x * scale + area_rect.x;
rects[i].y = rects[i].y * scale + area_rect.y;
rects[i].x = rects[i].x * scale + area.x;
rects[i].y = rects[i].y * scale + area.y;
slots.push([rects[i].x, rects[i].y, scale, clones[i]]);
targets[i] = [rects[i].x, rects[i].y, scale];
}
return slots;
return [clones, targets];
}
};
workspaceInjections['_calculateWindowTransformationsNatural'] = undefined;
let winInjections, workspaceInjections;
/**
* _realPositionWindows:
* @flags:
* INITIAL - this is the initial positioning of the windows.
* ANIMATE - Indicates that we need animate changing position.
*/
workspaceInjections['_realPositionWindows'] = Workspace.Workspace.prototype._realPositionWindows;
Workspace.Workspace.prototype._realPositionWindows = function(flags) {
if (this._repositionWindowsId > 0) {
Mainloop.source_remove(this._repositionWindowsId);
this._repositionWindowsId = 0;
}
function resetState() {
winInjections = { };
workspaceInjections = { };
}
let clones = this._windows.slice();
if (this._reservedSlot)
clones.push(this._reservedSlot);
function enable() {
resetState();
let initialPositioning = flags & WindowPositionFlags.INITIAL;
let animate = flags & WindowPositionFlags.ANIMATE;
let settings = Convenience.getSettings();
// Start the animations
let targets = [];
let scales = [];
workspaceInjections['_getBestLayout'] = Workspace.Workspace.prototype._getBestLayout;
Workspace.Workspace.prototype._getBestLayout = function(windows) {
let strategy = new NaturalLayoutStrategy(settings);
let layout = { strategy };
strategy.computeLayout(windows, layout);
[clones, targets] = this._calculateWindowTransformationsNatural(clones);
return layout;
let currentWorkspace = global.screen.get_active_workspace();
let isOnCurrentWorkspace = this.metaWorkspace == null || this.metaWorkspace == currentWorkspace;
for (let i = 0; i < clones.length; i++) {
let clone = clones[i];
let [x, y , scale] = targets[i];
let metaWindow = clone.metaWindow;
let mainIndex = this._lookupIndex(metaWindow);
let overlay = this._windowOverlays[mainIndex];
clone.slotId = i;
// Positioning a window currently being dragged must be avoided;
// we'll just leave a blank spot in the layout for it.
if (clone.inDrag)
continue;
clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale];
if (overlay && initialPositioning)
overlay.hide(initialPositioning);
if (animate && isOnCurrentWorkspace) {
if (!metaWindow.showing_on_its_workspace()) {
/* Hidden windows should fade in and grow
* therefore we need to resize them now so they
* can be scaled up later */
if (initialPositioning) {
clone.actor.opacity = 0;
clone.actor.scale_x = 0;
clone.actor.scale_y = 0;
clone.actor.x = x;
clone.actor.y = y;
}
// Make the window slightly transparent to indicate it's hidden
Tweener.addTween(clone.actor,
{ opacity: 255,
time: Overview.ANIMATION_TIME,
transition: 'easeInQuad'
});
}
this._animateClone(clone, overlay, x, y, scale, initialPositioning);
} else {
clone.actor.set_position(x, y);
clone.actor.set_scale(scale, scale);
clone.overlay.relayout(false);
this._showWindowOverlay(clone, overlay, isOnCurrentWorkspace);
}
}
}
/// position window titles on top of windows in overlay ////
winInjections['relayout'] = Workspace.WindowOverlay.prototype.relayout;
Workspace.WindowOverlay.prototype.relayout = function(animate) {
if (settings.get_boolean('window-captions-on-top')) {
let [, , , cloneHeight] = this._windowClone.slot;
this.title.translation_y = -cloneHeight;
}
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];
};
winInjections['relayout'].call(this, animate);
};
winInjections['updatePositions'] = Workspace.WindowOverlay.prototype.updatePositions;
Workspace.WindowOverlay.prototype.updatePositions = function(cloneX, cloneY, cloneWidth, cloneHeight, animate) {
let button = this.closeButton;
let title = this.title;
let settings = new Gio.Settings({ schema: BUTTON_LAYOUT_SCHEMA });
let layout = settings.get_string(BUTTON_LAYOUT_KEY);
let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
let split = layout.split(":");
let side;
if (split[0].indexOf("close") > -1)
side = rtl ? St.Side.RIGHT : St.Side.LEFT;
else
side = rtl ? St.Side.LEFT : St.Side.RIGHT;
let buttonX;
let buttonY = cloneY - (button.height - button._overlap);
if (side == St.Side.LEFT)
buttonX = cloneX - (button.width - button._overlap);
else
buttonX = cloneX + (cloneWidth - button._overlap);
if (animate)
this._animateOverlayActor(button, Math.floor(buttonX), Math.floor(buttonY), button.width);
else
button.set_position(Math.floor(buttonX), Math.floor(buttonY));
if (!title.fullWidth)
title.fullWidth = title.width;
let titleWidth = Math.min(title.fullWidth, cloneWidth);
let titleX = cloneX + (cloneWidth - titleWidth) / 2;
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));
}
};
}
}
function removeInjection(object, injection, name) {
@@ -283,13 +470,14 @@ function removeInjection(object, injection, name) {
}
function disable() {
var i;
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();
}

View File

@@ -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')

View File

@@ -4,7 +4,7 @@
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"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@" ],
"url": "@url@",
"original-authors": [ "wepmaschda@gmx.de" ]

View File

@@ -2,15 +2,15 @@
<schema id="org.gnome.shell.extensions.native-window-placement" path="/org/gnome/shell/extensions/native-window-placement/">
<key name="use-more-screen" type="b">
<default>true</default>
<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
them further to reduce the bounding box. This setting applies only with the natural placement strategy.</description>
<_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
them further to reduce the bounding box. This setting applies only with the natural placement strategy.</_description>
</key>
<key name="window-captions-on-top" type="b">
<default>true</default>
<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
the bottom. Changing this setting requires restarting the shell to have any effect.</description>
<_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
the bottom. Changing this setting requires restarting the shell to have any effect.</_description>
</key>
</schema>
</schemalist>

View File

@@ -0,0 +1,5 @@
EXTENSION_ID = places-menu
EXTRA_MODULES = placeDisplay.js
include ../../extension.mk

View File

@@ -1,9 +1,8 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
@@ -14,7 +13,7 @@ const Panel = imports.ui.panel;
const Gettext = imports.gettext.domain('gnome-shell-extensions');
const _ = Gettext.gettext;
const N_ = x => x;
const N_ = function(x) { return x; }
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
@@ -23,50 +22,45 @@ const PlaceDisplay = Me.imports.placeDisplay;
const PLACE_ICON_SIZE = 16;
class PlaceMenuItem extends PopupMenu.PopupBaseMenuItem {
constructor(info) {
super();
this._info = info;
const PlaceMenuItem = new Lang.Class({
Name: 'PlaceMenuItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function(info) {
this.parent();
this._info = info;
this._icon = new St.Icon({ gicon: info.icon,
icon_size: PLACE_ICON_SIZE });
this.actor.add_child(this._icon);
this.addActor(this._icon);
this._label = new St.Label({ text: info.name, x_expand: true });
this.actor.add_child(this._label);
if (info.isRemovable()) {
this._ejectIcon = new St.Icon({ icon_name: 'media-eject-symbolic',
style_class: 'popup-menu-icon ' });
this._ejectButton = new St.Button({ child: this._ejectIcon });
this._ejectButton.connect('clicked', info.eject.bind(info));
this.actor.add_child(this._ejectButton);
}
this._label = new St.Label({ text: info.name });
this.addActor(this._label);
this._changedId = info.connect('changed',
this._propertiesChanged.bind(this));
}
Lang.bind(this, this._propertiesChanged));
},
destroy() {
destroy: function() {
if (this._changedId) {
this._info.disconnect(this._changedId);
this._changedId = 0;
}
super.destroy();
}
this.parent();
},
activate(event) {
this._info.launch(event.get_time());
activate: function(event) {
this._info.launch(event.get_time());
super.activate(event);
}
this.parent(event);
},
_propertiesChanged(info) {
_propertiesChanged: function(info) {
this._icon.gicon = info.icon;
this._label.text = info.name;
}
};
},
});
const SECTIONS = [
'special',
@@ -75,54 +69,50 @@ const SECTIONS = [
'network'
]
let PlacesMenu = GObject.registerClass(
class PlacesMenu extends PanelMenu.Button {
_init() {
super._init(0.0, _("Places"));
const PlacesMenu = new Lang.Class({
Name: 'PlacesMenu.PlacesMenu',
Extends: PanelMenu.Button,
let hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
let label = new St.Label({ text: _("Places"),
y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
hbox.add_child(label);
hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
this.add_actor(hbox);
_init: function() {
let label = new St.Label({ text: _("Places") });
this.parent(0.0, label.text);
this.actor.add_actor(label);
this.placesManager = new PlaceDisplay.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', () => {
this._redisplay(id);
});
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);
}));
this._create(id);
this.menu.addMenuItem(this._sections[id]);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
}
}
this._create(id);
this.menu.addMenuItem(this._sections[id]);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
}
},
_onDestroy() {
this.placesManager.destroy();
destroy: function() {
this.placesManager.destroy();
super._onDestroy();
}
this.parent();
},
_redisplay(id) {
this._sections[id].removeAll();
_redisplay: function(id) {
this._sections[id].removeAll();
this._create(id);
}
},
_create(id) {
_create: function(id) {
let places = this.placesManager.get(id);
for (let i = 0; i < places.length; 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;
}
});
@@ -137,7 +127,7 @@ function enable() {
let pos = 1;
if ('apps-menu' in Main.panel.statusArea)
pos = 2;
pos = 2;
Main.panel.addToStatusArea('places-menu', _indicator, pos, 'left');
}

View File

@@ -1,7 +0,0 @@
extension_data += configure_file(
input: metadata_name + '.in',
output: metadata_name,
configuration: metadata_conf
)
extension_sources += files('placeDisplay.js')

View File

@@ -4,7 +4,7 @@
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"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@" ],
"url": "@url@"
}

View File

@@ -3,6 +3,7 @@
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;
@@ -11,247 +12,166 @@ const DND = imports.ui.dnd;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Search = imports.ui.search;
const ShellMountOperation = imports.ui.shellMountOperation;
const Util = imports.misc.util;
const Gettext = imports.gettext.domain('gnome-shell-extensions');
const _ = Gettext.gettext;
const N_ = x => x;
const N_ = function(x) { return x; }
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 Hostname1Iface = <interface name="org.freedesktop.hostname1">
<property name="PrettyHostname" type="s" access="read" />
</interface>;
const Hostname1 = Gio.DBusProxy.makeProxyWrapper(Hostname1Iface);
class PlaceInfo {
constructor() {
this._init.apply(this, arguments);
}
const PlaceInfo = new Lang.Class({
Name: 'PlaceInfo',
_init(kind, file, name, icon) {
_init: function(kind, file, name, icon) {
this.kind = kind;
this.file = file;
this.name = name || this._getFileName();
this.icon = icon ? new Gio.ThemedIcon({ name: icon }) : this.getIcon();
}
},
destroy() {
}
destroy: function() {
},
isRemovable() {
isRemovable: function() {
return false;
}
},
_createLaunchCallback(launchContext, tryMount) {
return (_ignored, result) => {
try {
Gio.AppInfo.launch_default_for_uri_finish(result);
} catch(e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED)) {
let source = {
get_icon: () => { return this.icon; }
};
let op = new ShellMountOperation.ShellMountOperation(source);
this.file.mount_enclosing_volume(0, op.mountOp, null, (file, result) => {
try {
op.close();
file.mount_enclosing_volume_finish(result);
} catch(e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
// e.g. user canceled the password dialog
return;
Main.notifyError(_("Failed to mount volume for “%s”").format(this.name), e.message);
return;
}
launch: function(timestamp) {
let launchContext = global.create_app_launch_context();
launchContext.set_timestamp(timestamp);
if (tryMount) {
let callback = this._createLaunchCallback(launchContext, false);
Gio.AppInfo.launch_default_for_uri_async(file.get_uri(),
launchContext,
null,
callback);
}
});
} else {
Main.notifyError(_("Failed to launch “%s”").format(this.name), e.message);
}
try {
Gio.AppInfo.launch_default_for_uri(this.file.get_uri(),
launchContext);
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED)) {
this.file.mount_enclosing_volume(0, null, null, function(file, result) {
file.mount_enclosing_volume_finish(result);
Gio.AppInfo.launch_default_for_uri(file.get_uri(), launchContext);
});
} catch(e) {
Main.notifyError(_("Failed to launch \"%s\"").format(this.name), e.message);
}
},
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' });
}
}
}
},
launch(timestamp) {
let launchContext = global.create_app_launch_context(timestamp, -1);
let callback = this._createLaunchCallback(launchContext, true);
Gio.AppInfo.launch_default_for_uri_async(this.file.get_uri(),
launchContext,
null,
callback);
}
getIcon() {
this.file.query_info_async('standard::symbolic-icon', 0, 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;
}
});
// return a generic icon for this kind for now, until we have the
// 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() {
_getFileName: function() {
try {
let info = this.file.query_info('standard::display-name', 0, null);
return info.get_display_name();
} catch(e) {
if (e instanceof Gio.IOErrorEnum)
return this.file.get_basename();
throw e;
} catch(e if e instanceof Gio.IOErrorEnum) {
return this.file.get_basename();
}
}
};
},
});
Signals.addSignalMethods(PlaceInfo.prototype);
class RootInfo extends PlaceInfo {
_init() {
super._init('devices', Gio.File.new_for_path('/'), _("Computer"));
const RootInfo = new Lang.Class({
Name: 'RootInfo',
Extends: PlaceInfo,
let busName = 'org.freedesktop.hostname1';
let objPath = '/org/freedesktop/hostname1';
new Hostname1(Gio.DBus.system, busName, objPath, (obj, error) => {
if (error)
return;
_init: function() {
this.parent('devices', Gio.File.new_for_path('/'), _("Computer"));
this._proxy = obj;
this._proxy.connect('g-properties-changed',
this._propertiesChanged.bind(this));
this._propertiesChanged(obj);
});
}
this._proxy = new Hostname1(Gio.DBus.system,
'org.freedesktop.hostname1',
'/org/freedesktop/hostname1',
Lang.bind(this, function(obj, error) {
if (error)
return;
getIcon() {
this._proxy.connect('g-properties-changed',
Lang.bind(this, this._propertiesChanged));
this._propertiesChanged(obj);
}));
},
getIcon: function() {
return new Gio.ThemedIcon({ name: 'drive-harddisk-symbolic' });
}
},
_propertiesChanged(proxy) {
_propertiesChanged: function(proxy) {
// GDBusProxy will emit a g-properties-changed when hostname1 goes down
// ignore it
if (proxy.g_name_owner) {
this.name = proxy.PrettyHostname || _("Computer");
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 {
_init(kind, mount) {
const PlaceDeviceInfo = new Lang.Class({
Name: 'PlaceDeviceInfo',
Extends: PlaceInfo,
_init: function(kind, 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();
}
});
isRemovable() {
return this._mount.can_eject();
}
const PlaceVolumeInfo = new Lang.Class({
Name: 'PlaceVolumeInfo',
Extends: PlaceInfo,
eject() {
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
this._ejectFinish.bind(this));
else
this._mount.unmount_with_operation(Gio.MountUnmountFlags.NONE,
mountOp.mountOp,
null, // Gio.Cancellable
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) {
_init: function(kind, 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) {
super.launch(timestamp);
this.parent(timestamp);
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);
let mount = volume.get_mount();
this.file = mount.get_root();
super.launch(timestamp);
});
}
this.parent(timestamp);
}));
},
getIcon() {
getIcon: function() {
return this._volume.get_symbolic_icon();
}
};
});
const DEFAULT_DIRECTORIES = [
GLib.UserDirectory.DIRECTORY_DOCUMENTS,
@@ -261,8 +181,10 @@ const DEFAULT_DIRECTORIES = [
GLib.UserDirectory.DIRECTORY_VIDEOS,
];
var PlacesManager = class {
constructor() {
const PlacesManager = new Lang.Class({
Name: 'PlacesManager',
_init: function() {
this._places = {
special: [],
devices: [],
@@ -270,11 +192,32 @@ var PlacesManager = class {
network: [],
};
this._settings = new Gio.Settings({ schema_id: BACKGROUND_SCHEMA });
this._showDesktopIconsChangedId =
this._settings.connect('changed::show-desktop-icons',
this._updateSpecials.bind(this));
this._updateSpecials();
let homePath = GLib.get_home_dir();
this._places.special.push(new PlaceInfo('special',
Gio.File.new_for_path(homePath),
_("Home")));
let specials = [];
for (let i = 0; i < DEFAULT_DIRECTORIES.length; i++) {
let specialPath = GLib.get_user_special_dir(DEFAULT_DIRECTORIES[i]);
if (specialPath == homePath)
continue;
let file = Gio.File.new_for_path(specialPath), info;
try {
info = new PlaceInfo('special', file);
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) {
continue;
}
specials.push(info);
}
specials.sort(function(a, b) {
return GLib.utf8_collate(a.name, b.name);
});
this._places.special = this._places.special.concat(specials);
/*
* Show devices, code more or less ported from nautilus-places-sidebar.c
@@ -289,39 +232,35 @@ var PlacesManager = class {
if (this._bookmarksFile) {
this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
this._monitor.connect('changed', () => {
this._monitor.connect('changed', Lang.bind(this, function () {
if (this._bookmarkTimeoutId > 0)
return;
/* Defensive event compression */
this._bookmarkTimeoutId = Mainloop.timeout_add(100, () => {
this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () {
this._bookmarkTimeoutId = 0;
this._reloadBookmarks();
return false;
});
});
}));
}));
this._reloadBookmarks();
}
}
},
_connectVolumeMonitorSignals() {
_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 = this._updateMounts.bind(this);
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() {
if (this._settings)
this._settings.disconnect(this._showDesktopIconsChangedId);
this._settings = null;
},
destroy: function() {
for (let i = 0; i < this._volumeMonitorSignals.length; i++)
this._volumeMonitor.disconnect(this._volumeMonitorSignals[i]);
@@ -329,54 +268,15 @@ var PlacesManager = class {
this._monitor.cancel();
if (this._bookmarkTimeoutId)
Mainloop.source_remove(this._bookmarkTimeoutId);
}
},
_updateSpecials() {
this._places.special.forEach(p => { p.destroy(); });
this._places.special = [];
let homePath = GLib.get_home_dir();
this._places.special.push(new PlaceInfo('special',
Gio.File.new_for_path(homePath),
_("Home")));
let specials = [];
let dirs = DEFAULT_DIRECTORIES.slice();
if (this._settings.get_boolean('show-desktop-icons'))
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 == null || specialPath == homePath)
continue;
let file = Gio.File.new_for_path(specialPath), info;
try {
info = new PlaceInfo('special', file);
} catch(e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
continue;
throw e;
}
specials.push(info);
}
specials.sort((a, b) => GLib.utf8_collate(a.name, b.name));
this._places.special = this._places.special.concat(specials);
this.emit('special-updated');
}
_updateMounts() {
_updateMounts: function() {
let networkMounts = [];
let networkVolumes = [];
this._places.devices.forEach(p => { p.destroy(); });
this._places.devices.forEach(function (p) { p.destroy(); });
this._places.devices = [];
this._places.network.forEach(p => { p.destroy(); });
this._places.network.forEach(function (p) { p.destroy(); });
this._places.network = [];
/* Add standard places */
@@ -392,9 +292,8 @@ var PlacesManager = class {
let volumes = drives[i].get_volumes();
for(let j = 0; j < volumes.length; j++) {
let identifier = volumes[j].get_identifier('class');
if (identifier && identifier.includes('network')) {
networkVolumes.push(volumes[j]);
if (volumes[j].get_identifier('class').indexOf('network') >= 0) {
networkVolumes.push(volumes[i]);
} else {
let mount = volumes[j].get_mount();
if(mount != null)
@@ -409,8 +308,7 @@ var PlacesManager = class {
if(volumes[i].get_drive() != null)
continue;
let identifier = volumes[i].get_identifier('class');
if (identifier && identifier.includes('network')) {
if (volumes[i].get_identifier('class').indexOf('network') >= 0) {
networkVolumes.push(volumes[i]);
} else {
let mount = volumes[i].get_mount();
@@ -451,9 +349,9 @@ var PlacesManager = class {
this.emit('devices-updated');
this.emit('network-updated');
}
},
_findBookmarksFile() {
_findBookmarksFile: function() {
let paths = [
GLib.build_filenamev([GLib.get_user_config_dir(), 'gtk-3.0', 'bookmarks']),
GLib.build_filenamev([GLib.get_home_dir(), '.gtk-bookmarks']),
@@ -465,9 +363,9 @@ var PlacesManager = class {
}
return null;
}
},
_reloadBookmarks() {
_reloadBookmarks: function() {
this._bookmarks = [];
@@ -515,38 +413,34 @@ var PlacesManager = class {
this._places.bookmarks = bookmarks;
this.emit('bookmarks-updated');
}
},
_addMount(kind, mount) {
_addMount: function(kind, mount) {
let devItem;
try {
devItem = new PlaceDeviceInfo(kind, mount);
} catch(e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
return;
throw e;
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) {
return;
}
this._places[kind].push(devItem);
}
},
_addVolume(kind, volume) {
_addVolume: function(kind, volume) {
let volItem;
try {
volItem = new PlaceVolumeInfo(kind, volume);
} catch(e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
return;
throw e;
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) {
return;
}
this._places[kind].push(volItem);
}
},
get(kind) {
get: function (kind) {
return this._places[kind];
}
};
});
Signals.addSignalMethods(PlacesManager.prototype);

View File

@@ -1,160 +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.
*/
const Gio = imports.gi.Gio;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;
const MESSAGE_FADE_TIME = 2;
let text, button;
function hideMessage() {
text.destroy();
text = null;
}
function flashMessage(message) {
if (!text) {
text = new St.Label({ style_class: 'screenshot-sizer-message' });
Main.uiGroup.add_actor(text);
}
Tweener.removeTweens(text);
text.text = message;
text.opacity = 255;
let monitor = Main.layoutManager.primaryMonitor;
text.set_position(monitor.x + Math.floor(monitor.width / 2 - text.width / 2),
monitor.y + Math.floor(monitor.height / 2 - text.height / 2));
Tweener.addTween(text,
{ opacity: 0,
time: MESSAGE_FADE_TIME,
transition: 'easeOutQuad',
onComplete: hideMessage });
}
let SIZES = [
[624, 351],
[800, 450],
[1024, 576],
[1200, 675],
[1600, 900]
];
function cycleScreenshotSizes(display, window, binding) {
// Probably this isn't useful with 5 sizes, but you can decrease instead
// of increase by holding down shift.
let modifiers = binding.get_modifiers();
let backwards = (modifiers & Meta.VirtualModifier.SHIFT_MASK) != 0;
// Unmaximize first
if (window.maximized_horizontally || window.maximizedVertically)
window.unmaximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL);
let workArea = window.get_work_area_current_monitor();
let outerRect = window.get_frame_rect();
// Double both axes if on a hidpi display
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let scaledSizes = SIZES.map(size => size.map(wh => wh * scaleFactor));
// 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];
// ignore sizes bigger than the workArea
if (width > workArea.width || height > workArea.height)
continue;
// get the best initial window size
let error = Math.abs(width - outerRect.width) + Math.abs(height - outerRect.height);
if (nearestIndex == null || 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;
[newWidth, newHeight] = scaledSizes[newIndex];
if (newWidth > workArea.width || newHeight > workArea.height)
[newWidth, newHeight] = scaledSizes[0];
// 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);
window.move_resize_frame(true, newX, newY, newWidth, newHeight);
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 = (newOuterRect.width / newOuterRect.height) * 9;
if (Math.abs(actualNumerator - 16) > 0.01)
message += ' (%.2f:9)'.format(actualNumerator);
flashMessage(message);
}
function init() {
}
function enable() {
Main.wm.addKeybinding('cycle-screenshot-sizes',
Convenience.getSettings(),
Meta.KeyBindingFlags.PER_WINDOW,
Shell.ActionMode.NORMAL,
cycleScreenshotSizes);
Main.wm.addKeybinding('cycle-screenshot-sizes-backward',
Convenience.getSettings(),
Meta.KeyBindingFlags.PER_WINDOW |
Meta.KeyBindingFlags.IS_REVERSED,
Shell.ActionMode.NORMAL,
cycleScreenshotSizes);
}
function disable() {
Main.wm.removeKeybinding('cycle-screenshot-sizes');
Main.wm.removeKeybinding('cycle-screenshot-sizes-backward');
}

View File

@@ -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')

View File

@@ -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>

View File

@@ -0,0 +1,3 @@
EXTENSION_ID = static-workspaces
include ../../extension.mk

View File

@@ -0,0 +1,18 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
const Meta = imports.gi.Meta;
function init(metadata) {
}
function enable() {
// Override gnome-shell's overrides
Meta.prefs_override_preference_schema('dynamic-workspaces',
'org.gnome.mutter');
}
function disable() {
// Restore gnome-shell's overrides
Meta.prefs_override_preference_schema('dynamic-workspaces',
'org.gnome.shell.overrides');
}

View File

@@ -3,8 +3,9 @@
"uuid": "@uuid@",
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"name": "Screenshot Window Sizer",
"description": "Resize windows for GNOME Software screenshots",
"name": "Static workspaces",
"description": "Disable dynamic workspace management",
"original-authors": [ "fmuellner@gnome.org" ],
"shell-version": [ "@shell_current@" ],
"url": "@url@"
}

View File

@@ -0,0 +1 @@
/* This extensions requires no special styling */

View File

@@ -0,0 +1,3 @@
EXTENSION_ID = systemMonitor
include ../../extension.mk

View File

@@ -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();
}

View File

@@ -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@"
}

View File

@@ -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;
}

View File

@@ -0,0 +1,5 @@
EXTENSION_ID = user-theme
include ../../extension.mk
include ../../settings.mk

View File

@@ -3,6 +3,7 @@
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Main = imports.ui.main;
const SETTINGS_KEY = 'name';
@@ -11,17 +12,19 @@ const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;
class ThemeManager {
constructor() {
const ThemeManager = new Lang.Class({
Name: 'UserTheme.ThemeManager',
_init: function() {
this._settings = Convenience.getSettings();
}
},
enable() {
this._changedId = this._settings.connect('changed::'+SETTINGS_KEY, this._changeTheme.bind(this));
enable: function() {
this._changedId = this._settings.connect('changed::'+SETTINGS_KEY, Lang.bind(this, this._changeTheme));
this._changeTheme();
}
},
disable() {
disable: function() {
if (this._changedId) {
this._settings.disconnect(this._changedId);
this._changedId = 0;
@@ -29,9 +32,9 @@ class ThemeManager {
Main.setThemeStylesheet(null);
Main.loadTheme();
}
},
_changeTheme() {
_changeTheme: function() {
let _stylesheet = null;
let _themeName = this._settings.get_string(SETTINGS_KEY);
@@ -42,7 +45,6 @@ class ThemeManager {
_stylesheet = _userCssStylesheet;
else {
let sysdirs = GLib.get_system_data_dirs();
sysdirs.unshift(GLib.get_user_data_dir());
for (let i = 0; i < sysdirs.length; i++) {
_userCssStylesheet = sysdirs[i] + '/themes/' + _themeName + '/gnome-shell/gnome-shell.css';
let file = Gio.file_new_for_path(_userCssStylesheet);
@@ -61,7 +63,7 @@ class ThemeManager {
Main.setThemeStylesheet(_stylesheet);
Main.loadTheme();
}
};
});
function init() {
return new ThemeManager();

View File

@@ -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')

Some files were not shown because too many files have changed in this diff Show More