Compare commits

...

26 Commits

Author SHA1 Message Date
Ray Strode
0a935b13b8 shellEntry: Support lockdown of "Show Text" menu in password entries
Some deployments require being able to prevent users from showing
the password they're currently typing.

This commit adds support for that kind of lockdown.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/687
2019-12-21 03:49:22 +01:00
Florian Müllner
f5f9bd2e5e iconGrid: Fix icon alignment
We don't want the icon to fill extra space, so set the alignment
accordingly. Otherwise we get an unexpected result when adding
a background just to the icon part (as far as I can tell: just
system-action-icon).

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/909
2019-12-20 22:58:26 +01:00
Florian Müllner
15d74c9cd4 st: Fix generated GLSL dependency
StScrollViewFade depends on st-scroll-view-fade-generated.c, but
that dependency isn't expressed to the build system; we just hope
that the custom target runs before compiling the effect.

Instead, add the generated source to the st target so the dependency
is expressed properly.

(The change from .c to .h is to prevent the file from being both
included and compiled, resulting in a duplicated symbol)

https://bugzilla.gnome.org/show_bug.cgi?id=789937
2019-12-20 21:02:45 +01:00
Florian Müllner
3c87ad5aab screenshot: Promisify PickPixel
Same as the previous commit, but for PickPixel.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/903
2019-12-20 15:43:01 +01:00
Florian Müllner
9db62236da screenshot: Promisify SelectArea
The screenshot code has a fair bit of nested callbacks, which means
that it is a good use case for async functions and Promises.

Most code uses GIO's async pattern, which means it can be easily turned
into promises with Gio._promisify(); first handle the couple of cases
that need custom code though, starting with SelectArea.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/903
2019-12-20 15:42:42 +01:00
Florian Müllner
93fa1034f5 grabHelper: Add (promised-based) grabAsync()
Some GrabHelper uses are in the form:

    doPreGrabStuff();

    this._grabHelper.grab({
        onUngrab: () => {
            undoPreGrabStuff();
        },
    });

A promise-based variant allows to write this more cleanly as:

    doPreGrabStuff();

    await this._grabHelper.grabAsync();

    undoPreGrabStuff();

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/903
2019-12-20 15:41:32 +01:00
Florian Müllner
35494f5d08 popupMenu: Close when source actor gets hidden
If a menu is anchored to a source actor, it is odd to leave it floating
around when the source actor gets hidden.

Close the menu instead in that case.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2002
2019-12-20 14:20:49 +00:00
Florian Müllner
4f66b301e7 panel: Disable menu-toggle shortcuts while top bar is hidden
We currently handle the case where the indicator itself is disabled
(read: hidden), but not when the entire top bar is invisible (for
instance when the primary monitor is in fullscreen state).

It is odd to pop up a top bar menu without the top bar, so check for
the indicator's mapped- instead of visible state.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/2002
2019-12-20 14:20:49 +00:00
Chuck
d1f87ca115 extension-tool: Don't treat missing .js handler as error
After creating a new extension, we try to open the main source
file with the default handler, which fails when there is none.

But given that the extension was created successfully, don't treat
a missing handler as failure, and print the path to the new extension
instead.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/905
2019-12-20 14:11:03 +00:00
Michael Catanzaro
fe106358f5 appFavorites: unbreak my previous commits
Oops, apparently even a trivial commit is too hard for me to do without CI.
2019-12-20 13:59:53 +00:00
Michael Catanzaro
1c3ad0f1cf Update desktop IDs of favorite apps
Epiphany and Evolution updated their desktop IDs years ago. I assume the
rename list in appFavorites.js ensures this is still working properly
despite the obsolete names.
2019-12-20 13:53:12 +00:00
Michael Catanzaro
89b3104f8f appFavorites: add seahorse to rename list 2019-12-20 13:49:07 +00:00
Florian Müllner
624cf1dad4 extensions-tool: Set up translations for standalone builds
While we can now build gnome-extensions-tool as stand-alone project,
we are currently missing any translations, as those are part of
gnome-shell.

The easiest option for addressing this would be to symlink the toplevel
po directory into the subproject, however that would mean duplicating
the entire gnome-shell message catalogs.

So instead, set up a bare po directory and provide a script to populate
it from the translations in the toplevel po directory.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/877
2019-12-18 19:13:24 +00:00
Florian Müllner
4e9154ca64 extensions-tool: Add (back) README.md
This was dropped when importing the project into gnome-shell. Now
that is a bit more separate again, it makes sense to add it back.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/877
2019-12-18 19:13:24 +00:00
Florian Müllner
51518d4d96 extensions-tool: Move to a subproject
The gnome-extensions tool code is really independent from the rest of the
code base, and could be used either as part of the gnome-shell build or as
stand-alone project (for example for the extension-ci docker image).

We can actually support both cases by moving the code to a subproject.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/877
2019-12-18 19:13:24 +00:00
Florian Müllner
26dc2a439d build: Remove left-over file
The file was generated when importing gnome-extensions-tool from
a standalone repository. It isn't used by the build system, and
really shouldn't have ended up in the repository at all.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/877
2019-12-18 19:13:24 +00:00
Georges Basile Stavracas Neto
bfd5fc3f24 doap: Add Georges Stavracas as a maintainer
Looks like he's is working too much in this module, and received
permission from Florian to add himself as a maintainer.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/901
2019-12-18 15:24:38 -03:00
Umang Jain
60721a7c23 StPasswordEntry: Add the peek-password-icon for show/hide passwords
Also introduce a "show-peek-icon" property to enable/disable
the peek-password-icon in the password entry. This is useful
in cases where the peeking the password functionality needs
to be avoided.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/619
2019-12-17 23:08:43 +01:00
Umang Jain
df230989b1 st-entry: Remove caps-lock feedback warning
This frees the entry's secondary icon that for other uses.
Caps-lock-warning feedback has been moved to be shown in
the various dialogs instead in the password-entries itself.
StPasswordEntry can now use a peek-password icon as the
secondary icon to show/hide the password present in the
entry.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/619
2019-12-17 23:08:43 +01:00
Umang Jain
474dda7ffe js: Add caps-lock Warning to the dialogs
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/619
2019-12-17 23:08:43 +01:00
Umang Jain
1d54f1e6ab shellEntry: Add CapsLockWarning class
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/619
2019-12-17 23:08:43 +01:00
Umang Jain
a1238a0ea4 shellEntry: Remove isPassword Property
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/619
2019-12-17 23:08:43 +01:00
Umang Jain
684b918915 js: Use StPasswordEntry for password entry fields
Use the new StPasswordEntry for password entry fields
and remove all direct handling of clutter text of the entry
 via clutter_text_set_password_char to show/hide the password
text. StPasswordEntry will provides a peek-password-icon which
will allow to show/hide the password present in the field to
the user in subsequent commits.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/619
2019-12-17 23:08:43 +01:00
Umang Jain
b166de08dc shellEntry: Handle PasswordEntries automatically
shellEntry should not need to query the clutter-text directly
in order to know if the entry is for passport-input purpose.
shellEntry can easily determine a password-entry by querying
if it is an instance of StPasswordEntry and use it's API
whereever relevant.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/619
2019-12-17 23:08:43 +01:00
Umang Jain
281c87d11b St: Add a StPasswordEntry subclass based on StEntry
StPasswordEntry will be put to use for password entries
in various shell dialogs. This is done to have a consistent
behaviour for all password entries and introduce a peek
password functionality for these password entries in the
subsequent commits.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/619
2019-12-17 23:08:43 +01:00
Carlos Garnacho
14eeaf4a2a padOsd: Re-query action labels after mode switches
Do this so the pad OSD is able to update dynamically to mode changes,
showing immediately the new actions for the current mode(s).

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/898
2019-12-17 13:07:39 +01:00
60 changed files with 797 additions and 299 deletions

View File

@@ -20,6 +20,8 @@
<file>no-notifications.svg</file>
<file>noise-texture.png</file>
<file>pad-osd.css</file>
<file alias="icons/eye-open-negative-filled-symbolic.svg">eye-open-negative-filled-symbolic.svg</file>
<file alias="icons/eye-not-looking-symbolic.svg">eye-not-looking-symbolic.svg</file>
<file alias="icons/pointer-double-click-symbolic.svg">pointer-double-click-symbolic.svg</file>
<file alias="icons/pointer-drag-symbolic.svg">pointer-drag-symbolic.svg</file>
<file alias="icons/pointer-primary-click-symbolic.svg">pointer-primary-click-symbolic.svg</file>

View File

@@ -50,7 +50,7 @@
</description>
</key>
<key name="favorite-apps" type="as">
<default>[ 'epiphany.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'org.gnome.Shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default>
<default>[ 'org.gnome.Epiphany.desktop', 'org.gnome.Evolution.desktop', 'rhythmbox.desktop', 'org.gnome.Shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default>
<summary>List of desktop file IDs for favorite applications</summary>
<description>
The applications corresponding to these identifiers

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<path d="M13.98 1.99a1 1 0 0 0-.687.303l-.984.984A8 8 0 0 0 8 2 8 8 0 0 0 .262 8.01a8 8 0 0 0 2.943 4.37l-.912.913a1 1 0 1 0 1.414 1.414l11-11a1 1 0 0 0-.727-1.717zM8 4a4 4 0 0 1 2.611.974l-1.42 1.42A2 2 0 0 0 8 6a2 2 0 0 0-2 2 2 2 0 0 0 .396 1.19l-1.42 1.42A4 4 0 0 1 4 8a4 4 0 0 1 4-4zm7.03 2.209l-3.344 3.343a4 4 0 0 1-2.127 2.127l-2.28 2.28a8 8 0 0 0 .721.04 8 8 0 0 0 7.738-6.01 8 8 0 0 0-.709-1.78zm-7.53.79a.5.5 0 0 1 .5.5.5.5 0 0 1-.5.5.5.5 0 0 1-.5-.5.5.5 0 0 1 .5-.5z" fill="#2e3436"/>
</svg>

After

Width:  |  Height:  |  Size: 572 B

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" 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" width="16" viewBox="0 0 16 16" version="1.1" id="svg7384" height="16">
<metadata id="metadata90">
<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>Gnome Symbolic Icon Theme</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<title id="title9167">Gnome Symbolic Icon Theme</title>
<defs id="defs7386">
<linearGradient osb:paint="solid" id="linearGradient7212">
<stop style="stop-color:#000000;stop-opacity:1;" offset="0" id="stop7214"/>
</linearGradient>
</defs>
<g transform="translate(-341.0002,-13.000323)" style="display:inline" id="layer9"/>
<g transform="translate(-100,-380.00032)" id="layer1"/>
<g transform="translate(-100,-380.00032)" style="display:inline" id="layer10">
<path d="m 108,382 a 8,8 0 0 0 -7.73828,6.00977 A 8,8 0 0 0 108,394 8,8 0 0 0 115.73828,387.99023 8,8 0 0 0 108,382 Z m 0,2 a 4,4 0 0 1 4,4 4,4 0 0 1 -4,4 4,4 0 0 1 -4,-4 4,4 0 0 1 4,-4 z" id="path2314" style="opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:round;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"/>
<path id="path2318" d="m 110,388.00003 a 2,2 0 0 1 -2,2 2,2 0 0 1 -2,-2 2,2 0 0 1 2,-2 2,2 0 0 1 2,2 z" style="vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/>
</g>
<g transform="translate(-100,-380.00032)" id="g6387"/>
<g transform="translate(-100,-380.00032)" id="layer11"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -97,6 +97,11 @@ StEntry {
warning-color: $warning_color;
padding: 0 4px;
}
StIcon.peek-password {
icon-size: 1.09em;
padding: 0 4px;
}
}
@@ -404,6 +409,11 @@ StScrollBar {
padding-bottom: 8px;
}
.prompt-dialog-caps-lock-warning {
@extend .prompt-dialog-error-label;
padding-left: 6.2em;
}
.prompt-dialog-info-label {
font-size: 10pt;
padding-bottom: 8px;

View File

@@ -66,4 +66,11 @@ its dependencies to build from tarballs.</description>
<gnome:userid>fmuellner</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Georges Basile Stavracas Neto</foaf:name>
<foaf:mbox rdf:resource="mailto:gbsneto@gnome.org" />
<gnome:userid>gbsneto</gnome:userid>
</foaf:Person>
</maintainer>
</Project>

View File

@@ -98,18 +98,30 @@ var AuthPrompt = GObject.registerClass({
});
this.add_child(this._label);
this._entry = new St.Entry({
let entryParams = {
style_class: 'login-dialog-prompt-entry',
can_focus: true,
x_expand: false,
y_expand: true,
});
ShellEntry.addContextMenu(this._entry, { isPassword: true, actionMode: Shell.ActionMode.NONE });
};
this._entry = null;
this._textEntry = new St.Entry(entryParams);
ShellEntry.addContextMenu(this._textEntry, { actionMode: Shell.ActionMode.NONE });
this._passwordEntry = new St.PasswordEntry(entryParams);
ShellEntry.addContextMenu(this._passwordEntry, { actionMode: Shell.ActionMode.NONE });
this._entry = this._passwordEntry;
this.add_child(this._entry);
this._entry.grab_key_focus();
this._capsLockWarningLabel = new ShellEntry.CapsLockWarning();
this.add_child(this._capsLockWarningLabel);
this._message = new St.Label({
opacity: 0,
styleClass: 'login-dialog-message',
@@ -195,7 +207,18 @@ var AuthPrompt = GObject.registerClass({
});
}
_onAskQuestion(verifier, serviceName, question, passwordChar) {
_updateEntry(secret) {
if (secret && (this._entry != this._passwordEntry)) {
this.replace_child(this._entry, this._passwordEntry);
this._entry = this._passwordEntry;
} else if (!secret && (this._entry != this._textEntry)) {
this.replace_child(this._entry, this._textEntry);
this._entry = this._textEntry;
}
this._capsLockWarningLabel.visible = secret;
}
_onAskQuestion(verifier, serviceName, question, secret) {
if (this._queryingService)
this.clear();
@@ -205,10 +228,11 @@ var AuthPrompt = GObject.registerClass({
this._preemptiveAnswer = null;
return;
}
this.setPasswordChar(passwordChar);
this._updateEntry(secret);
this.setQuestion(question);
if (passwordChar) {
if (secret) {
if (this._userVerifier.reauthenticating)
this.nextButton.label = _("Unlock");
else
@@ -358,11 +382,6 @@ var AuthPrompt = GObject.registerClass({
this.stopSpinning();
}
setPasswordChar(passwordChar) {
this._entry.clutter_text.set_password_char(passwordChar);
this._entry.menu.isPassword = passwordChar != '';
}
setQuestion(question) {
this._label.set_text(question);

View File

@@ -896,7 +896,6 @@ var LoginDialog = GObject.registerClass({
}
_askForUsernameAndBeginVerification() {
this._authPrompt.setPasswordChar('');
this._authPrompt.setQuestion(_("Username: "));
this._showRealmLoginHint(this._realmManager.loginFormat);

View File

@@ -485,7 +485,7 @@ var ShellUserVerifier = class {
if (!this.serviceIsForeground(serviceName))
return;
this.emit('ask-question', serviceName, question, '');
this.emit('ask-question', serviceName, question, false);
}
_onSecretInfoQuery(client, serviceName, secretQuestion) {
@@ -498,7 +498,7 @@ var ShellUserVerifier = class {
return;
}
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
this.emit('ask-question', serviceName, secretQuestion, true);
}
_onReset() {

View File

@@ -55,6 +55,7 @@ const RENAMED_DESKTOP_IDS = {
'org.gnome.taquin.desktop': 'org.gnome.Taquin.desktop',
'org.gnome.Weather.Application.desktop': 'org.gnome.Weather.desktop',
'polari.desktop': 'org.gnome.Polari.desktop',
'seahorse.desktop': 'org.gnome.seahorse.Application.desktop',
'shotwell.desktop': 'org.gnome.Shotwell.desktop',
'tali.desktop': 'org.gnome.Tali.desktop',
'totem.desktop': 'org.gnome.Totem.desktop',

View File

@@ -70,12 +70,13 @@ class KeyringDialog extends ModalDialog.ModalDialog {
y_align: Clutter.ActorAlign.CENTER });
label.set_text(_("Password:"));
label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: '',
can_focus: true,
x_expand: true });
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
this._passwordEntry = new St.PasswordEntry({
style_class: 'prompt-dialog-password-entry',
text: '',
can_focus: true,
x_expand: true,
});
ShellEntry.addContextMenu(this._passwordEntry);
this._passwordEntry.clutter_text.connect('activate', this._onPasswordActivate.bind(this));
this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, {
@@ -102,12 +103,13 @@ class KeyringDialog extends ModalDialog.ModalDialog {
x_align: Clutter.ActorAlign.START,
y_align: Clutter.ActorAlign.CENTER });
label.set_text(_("Type again:"));
this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: '',
can_focus: true,
x_expand: true });
this._confirmEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
ShellEntry.addContextMenu(this._confirmEntry, { isPassword: true });
this._confirmEntry = new St.PasswordEntry({
style_class: 'prompt-dialog-password-entry',
text: '',
can_focus: true,
x_expand: true,
});
ShellEntry.addContextMenu(this._confirmEntry);
this._confirmEntry.clutter_text.connect('activate', this._onConfirmActivate.bind(this));
if (rtl) {
layout.attach(this._confirmEntry, 0, row, 1, 1);
@@ -124,6 +126,12 @@ class KeyringDialog extends ModalDialog.ModalDialog {
this.prompt.set_password_actor(this._passwordEntry ? this._passwordEntry.clutter_text : null);
this.prompt.set_confirm_actor(this._confirmEntry ? this._confirmEntry.clutter_text : null);
if (this._passwordEntry || this._confirmEntry) {
this._capsLockWarningLabel = new ShellEntry.CapsLockWarning();
layout.attach(this._capsLockWarningLabel, 1, row, 1, 1);
row++;
}
if (this.prompt.choice_visible) {
let choice = new CheckBox.CheckBox();
this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE);

View File

@@ -54,12 +54,18 @@ class NetworkSecretDialog extends ModalDialog.ModalDialog {
let reactive = secret.key != null;
secret.entry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: secret.value, can_focus: reactive,
reactive,
x_expand: true });
ShellEntry.addContextMenu(secret.entry,
{ isPassword: secret.password });
let entryParams = {
style_class: 'prompt-dialog-password-entry',
text: secret.value,
can_focus: reactive,
reactive,
x_expand: true,
};
if (secret.password)
secret.entry = new St.PasswordEntry(entryParams);
else
secret.entry = new St.Entry(entryParams);
ShellEntry.addContextMenu(secret.entry);
if (secret.validate)
secret.valid = secret.validate(secret);
@@ -93,9 +99,14 @@ class NetworkSecretDialog extends ModalDialog.ModalDialog {
layout.attach(secret.entry, 1, pos, 1, 1);
}
pos++;
}
if (secret.password)
secret.entry.clutter_text.set_password_char('\u25cf');
if (this._content.secrets.some(s => s.password)) {
this._capsLockWarningLabel = new ShellEntry.CapsLockWarning();
if (rtl)
layout.attach(this._capsLockWarningLabel, 0, pos, 1, 1);
else
layout.attach(this._capsLockWarningLabel, 1, pos, 1, 1);
}
contentBox.messageBox.add(secretTable);

View File

@@ -90,13 +90,13 @@ var AuthenticationDialog = GObject.registerClass({
y_align: Clutter.ActorAlign.CENTER,
});
this._passwordBox.add_child(this._passwordLabel);
this._passwordEntry = new St.Entry({
this._passwordEntry = new St.PasswordEntry({
style_class: 'prompt-dialog-password-entry',
text: "",
can_focus: true,
x_expand: true,
});
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
ShellEntry.addContextMenu(this._passwordEntry);
this._passwordEntry.clutter_text.connect('activate', this._onEntryActivate.bind(this));
this._passwordEntry.bind_property('reactive',
this._passwordEntry.clutter_text, 'editable',
@@ -109,6 +109,8 @@ var AuthenticationDialog = GObject.registerClass({
this._passwordBox.add(this._workSpinner);
this._passwordBox.hide();
this._capsLockWarningLabel = new ShellEntry.CapsLockWarning({ style_class: 'prompt-dialog-caps-lock-warning' });
content.messageBox.add(this._capsLockWarningLabel);
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' });
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
@@ -278,10 +280,7 @@ var AuthenticationDialog = GObject.registerClass({
else
this._passwordLabel.set_text(request);
if (echoOn)
this._passwordEntry.clutter_text.set_password_char('');
else
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
this._passwordEntry.password_visible = echoOn;
this._passwordBox.show();
this._passwordEntry.set_text('');

View File

@@ -194,6 +194,15 @@ var GrabHelper = class GrabHelper {
return true;
}
grabAsync(params) {
return new Promise((resolve, reject) => {
params.onUngrab = resolve;
if (!this.grab(params))
reject(new Error('Grab failed'));
});
}
_takeModalGrab() {
let firstGrab = this._modalCount == 0;
if (firstGrab) {

View File

@@ -51,7 +51,7 @@ class BaseIcon extends St.Bin {
this.set_child(this._box);
this.iconSize = ICON_SIZE;
this._iconBin = new St.Bin();
this._iconBin = new St.Bin({ x_align: Clutter.ActorAlign.CENTER });
this._box.add_actor(this._iconBin);

View File

@@ -564,6 +564,14 @@ var PadDiagram = GObject.registerClass({
this.add_actor(label);
}
updateLabels(getText) {
for (let i = 0; i < this._labels.length; i++) {
let [label, action, idx, dir] = this._labels[i];
let str = getText(action, idx, dir);
label.set_text(str);
}
}
_applyLabel(label, action, idx, dir, str) {
if (str != null) {
label.set_text(str);
@@ -776,17 +784,29 @@ var PadOsd = GObject.registerClass({
global.display.request_pad_osd(pad, editionMode);
}
_createLabel(type, number, dir) {
_getActionText(type, number) {
let str = global.display.get_pad_action_label(this.padDevice, type, number);
let label = new St.Label({ text: str ? str : _("None") });
return str ? str : _("None");
}
_createLabel(type, number, dir) {
let label = new St.Label({ text: this._getActionText(type, number) });
this._padDiagram.addLabel(label, type, number, dir);
}
_updateActionLabels() {
this._padDiagram.updateLabels(this._getActionText.bind(this));
}
_onCapturedEvent(actor, event) {
let isModeSwitch =
(event.type() == Clutter.EventType.PAD_BUTTON_PRESS ||
event.type() == Clutter.EventType.PAD_BUTTON_RELEASE) &&
this.padDevice.get_mode_switch_button_group(event.get_button()) >= 0;
if (event.type() == Clutter.EventType.PAD_BUTTON_PRESS &&
event.get_source_device() == this.padDevice) {
this._padDiagram.activateButton(event.get_button());
let isModeSwitch = this.padDevice.get_mode_switch_button_group(event.get_button()) >= 0;
/* Buttons that switch between modes cannot be edited */
if (this._editionMode && !isModeSwitch)
@@ -795,6 +815,11 @@ var PadOsd = GObject.registerClass({
} else if (event.type() == Clutter.EventType.PAD_BUTTON_RELEASE &&
event.get_source_device() == this.padDevice) {
this._padDiagram.deactivateButton(event.get_button());
if (isModeSwitch) {
this._endActionEdition();
this._updateActionLabels();
}
return Clutter.EVENT_STOP;
} else if (event.type() == Clutter.EventType.KEY_PRESS &&
(!this._editionMode || event.get_key_symbol() === Clutter.KEY_Escape)) {

View File

@@ -962,7 +962,7 @@ class Panel extends St.Widget {
}
_toggleMenu(indicator) {
if (!indicator || !indicator.container.visible)
if (!indicator || !indicator.mapped)
return; // menu not supported by current session mode
let menu = indicator.menu;

View File

@@ -814,7 +814,12 @@ var PopupMenu = class extends PopupMenuBase {
if (this.sourceActor) {
this._keyPressId = this.sourceActor.connect('key-press-event',
this._onKeyPress.bind(this));
this._onKeyPress.bind(this));
this._notifyMappedId = this.sourceActor.connect('notify::mapped',
() => {
if (!this.sourceActor.mapped)
this.close();
});
}
this._systemModalOpenedId = 0;
@@ -928,6 +933,9 @@ var PopupMenu = class extends PopupMenuBase {
if (this._keyPressId)
this.sourceActor.disconnect(this._keyPressId);
if (this._notifyMappedId)
this.sourceActor.disconnect(this._notifyMappedId);
if (this._systemModalOpenedId)
Main.layoutManager.disconnect(this._systemModalOpenedId);
this._systemModalOpenedId = 0;

View File

@@ -227,20 +227,19 @@ var ScreenshotService = class {
});
}
SelectAreaAsync(params, invocation) {
async SelectAreaAsync(params, invocation) {
let selectArea = new SelectArea();
selectArea.show();
selectArea.connect('finished', (o, areaRectangle) => {
if (areaRectangle) {
let retRectangle = this._unscaleArea(areaRectangle.x, areaRectangle.y,
areaRectangle.width, areaRectangle.height);
let retval = GLib.Variant.new('(iiii)', retRectangle);
invocation.return_value(retval);
} else {
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
"Operation was cancelled");
}
});
try {
let areaRectangle = await selectArea.selectAsync();
let retRectangle = this._unscaleArea(
areaRectangle.x, areaRectangle.y,
areaRectangle.width, areaRectangle.height);
invocation.return_value(GLib.Variant.new('(iiii)', retRectangle));
} catch (e) {
invocation.return_error_literal(
Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
'Operation was cancelled');
}
}
FlashAreaAsync(params, invocation) {
@@ -257,38 +256,38 @@ var ScreenshotService = class {
invocation.return_value(null);
}
PickColorAsync(params, invocation) {
async PickColorAsync(params, invocation) {
let pickPixel = new PickPixel();
pickPixel.show();
pickPixel.connect('finished', (obj, coords) => {
if (coords) {
let screenshot = this._createScreenshot(invocation, false);
if (!screenshot)
return;
screenshot.pick_color(coords.x, coords.y, (_o, res) => {
let [success_, color] = screenshot.pick_color_finish(res);
let { red, green, blue } = color;
let retval = GLib.Variant.new('(a{sv})', [{
color: GLib.Variant.new('(ddd)', [
red / 255.0,
green / 255.0,
blue / 255.0,
]),
}]);
this._removeShooterForSender(invocation.get_sender());
invocation.return_value(retval);
});
} else {
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
"Operation was cancelled");
}
});
try {
const coords = await pickPixel.pickAsync();
let screenshot = this._createScreenshot(invocation, false);
if (!screenshot)
return;
screenshot.pick_color(coords.x, coords.y, (_o, res) => {
let [success_, color] = screenshot.pick_color_finish(res);
let { red, green, blue } = color;
let retval = GLib.Variant.new('(a{sv})', [{
color: GLib.Variant.new('(ddd)', [
red / 255.0,
green / 255.0,
blue / 255.0,
]),
}]);
this._removeShooterForSender(invocation.get_sender());
invocation.return_value(retval);
});
} catch (e) {
invocation.return_error_literal(
Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
'Operation was cancelled');
}
}
};
var SelectArea = GObject.registerClass({
Signals: { 'finished': { param_types: [Meta.Rectangle.$gtype] } },
}, class SelectArea extends St.Widget {
var SelectArea = GObject.registerClass(
class SelectArea extends St.Widget {
_init() {
this._startX = -1;
this._startY = -1;
@@ -317,14 +316,21 @@ var SelectArea = GObject.registerClass({
this.add_actor(this._rubberband);
}
vfunc_show() {
if (!this._grabHelper.grab({ actor: this,
onUngrab: this._onUngrab.bind(this) }))
return;
async selectAsync() {
global.display.set_cursor(Meta.Cursor.CROSSHAIR);
Main.uiGroup.set_child_above_sibling(this, null);
super.vfunc_show();
this.show();
await this._grabHelper.grabAsync({ actor: this });
global.display.set_cursor(Meta.Cursor.DEFAULT);
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
this.destroy();
return GLib.SOURCE_REMOVE;
});
return this._result;
}
_getGeometry() {
@@ -371,21 +377,10 @@ var SelectArea = GObject.registerClass({
});
return Clutter.EVENT_PROPAGATE;
}
_onUngrab() {
global.display.set_cursor(Meta.Cursor.DEFAULT);
this.emit('finished', this._result);
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
this.destroy();
return GLib.SOURCE_REMOVE;
});
}
});
var PickPixel = GObject.registerClass({
Signals: { 'finished': { param_types: [Graphene.Point.$gtype] } },
}, class PickPixel extends St.Widget {
var PickPixel = GObject.registerClass(
class PickPixel extends St.Widget {
_init() {
super._init({ visible: false, reactive: true });
@@ -400,14 +395,21 @@ var PickPixel = GObject.registerClass({
this.add_constraint(constraint);
}
vfunc_show() {
if (!this._grabHelper.grab({ actor: this,
onUngrab: this._onUngrab.bind(this) }))
return;
async pickAsync() {
global.display.set_cursor(Meta.Cursor.CROSSHAIR);
Main.uiGroup.set_child_above_sibling(this, null);
super.vfunc_show();
this.show();
await this._grabHelper.grabAsync({ actor: this });
global.display.set_cursor(Meta.Cursor.DEFAULT);
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
this.destroy();
return GLib.SOURCE_REMOVE;
});
return this._result;
}
vfunc_button_release_event(buttonEvent) {
@@ -416,16 +418,6 @@ var PickPixel = GObject.registerClass({
this._grabHelper.ungrab();
return Clutter.EVENT_PROPAGATE;
}
_onUngrab() {
global.display.set_cursor(Meta.Cursor.DEFAULT);
this.emit('finished', this._result);
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
this.destroy();
return GLib.SOURCE_REMOVE;
});
}
});
var FLASHSPOT_ANIMATION_OUT_TIME = 500; // milliseconds

View File

@@ -1,17 +1,24 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported addContextMenu */
/* exported addContextMenu CapsLockWarning */
const { Clutter, Shell, St } = imports.gi;
const { Clutter, Gio, GObject, Pango, Shell, St } = imports.gi;
const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main;
const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const DISABLE_SHOW_PASSWORD_KEY = 'disable-show-password';
var EntryMenu = class extends PopupMenu.PopupMenu {
constructor(entry) {
super(entry, 0, St.Side.TOP);
this._lockdownSettings = new Gio.Settings({ schema_id: LOCKDOWN_SCHEMA });
this._lockdownSettings.connect(`changed::${DISABLE_SHOW_PASSWORD_KEY}`,
this._applyLockdownSettings.bind(this));
this._entry = entry;
this._clipboard = St.Clipboard.get_default();
@@ -27,7 +34,8 @@ var EntryMenu = class extends PopupMenu.PopupMenu {
this.addMenuItem(item);
this._pasteItem = item;
this._passwordItem = null;
if (entry instanceof St.PasswordEntry)
this._makePasswordItem();
Main.uiGroup.add_actor(this.actor);
this.actor.hide();
@@ -38,24 +46,20 @@ var EntryMenu = class extends PopupMenu.PopupMenu {
item.connect('activate', this._onPasswordActivated.bind(this));
this.addMenuItem(item);
this._passwordItem = item;
this._applyLockdownSettings();
}
get isPassword() {
return this._passwordItem != null;
}
set isPassword(v) {
if (v == this.isPassword)
_applyLockdownSettings() {
if (!this._passwordItem)
return;
if (v) {
this._makePasswordItem();
this._entry.input_purpose = Clutter.InputContentPurpose.PASSWORD;
} else {
this._passwordItem.destroy();
this._passwordItem = null;
this._entry.input_purpose = Clutter.InputContentPurpose.NORMAL;
}
let passwordDisabled = this._lockdownSettings.get_boolean(DISABLE_SHOW_PASSWORD_KEY);
this._passwordItem.visible = !passwordDisabled;
this._entry.show_peek_icon = !passwordDisabled;
if (passwordDisabled)
this._entry.password_visible = false;
}
open(animate) {
@@ -86,8 +90,7 @@ var EntryMenu = class extends PopupMenu.PopupMenu {
}
_updatePasswordItem() {
let textHidden = this._entry.clutter_text.password_char;
if (textHidden)
if (!this._entry.password_visible)
this._passwordItem.label.set_text(_("Show Text"));
else
this._passwordItem.label.set_text(_("Hide Text"));
@@ -110,8 +113,7 @@ var EntryMenu = class extends PopupMenu.PopupMenu {
}
_onPasswordActivated() {
let visible = !!this._entry.clutter_text.password_char;
this._entry.clutter_text.set_password_char(visible ? '' : '\u25cf');
this._entry.password_visible = !this._entry.password_visible;
}
};
@@ -145,10 +147,9 @@ function addContextMenu(entry, params) {
if (entry.menu)
return;
params = Params.parse(params, { isPassword: false, actionMode: Shell.ActionMode.POPUP });
params = Params.parse(params, { actionMode: Shell.ActionMode.POPUP });
entry.menu = new EntryMenu(entry);
entry.menu.isPassword = params.isPassword;
entry._menuManager = new PopupMenu.PopupMenuManager(entry,
{ actionMode: params.actionMode });
entry._menuManager.addMenu(entry.menu);
@@ -171,3 +172,40 @@ function addContextMenu(entry, params) {
entry._menuManager = null;
});
}
var CapsLockWarning = GObject.registerClass(
class CapsLockWarning extends St.Label {
_init(params) {
let defaultParams = { style_class: 'prompt-dialog-error-label' };
super._init(Object.assign(defaultParams, params));
this.text = _('Caps lock is on.');
this._keymap = Clutter.get_default_backend().get_keymap();
this.connect('notify::mapped', () => {
if (this.is_mapped()) {
this.stateChangedId = this._keymap.connect('state-changed',
this._updateCapsLockWarningOpacity.bind(this));
} else {
this._keymap.disconnect(this.stateChangedId);
this.stateChangedId = 0;
}
this._updateCapsLockWarningOpacity();
});
this.connect('destroy', () => {
if (this.stateChangedId > 0)
this._keymap.disconnect(this.stateChangedId);
});
this.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this.clutter_text.line_wrap = true;
}
_updateCapsLockWarningOpacity() {
let capsLockOn = this._keymap.get_caps_lock_state();
this.opacity = capsLockOn ? 255 : 0;
}
});

View File

@@ -326,12 +326,13 @@ var ShellMountPasswordDialog = GObject.registerClass({
this._pimLabel = new St.Label({ style_class: 'prompt-dialog-password-label',
text: _("PIM Number"),
y_align: Clutter.ActorAlign.CENTER });
this._pimEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
can_focus: true,
x_expand: true });
this._pimEntry = new St.PasswordEntry({
style_class: 'prompt-dialog-password-entry',
can_focus: true,
x_expand: true,
});
this._pimEntry.clutter_text.connect('activate', this._onEntryActivate.bind(this));
this._pimEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
ShellEntry.addContextMenu(this._pimEntry, { isPassword: true });
ShellEntry.addContextMenu(this._pimEntry);
if (rtl) {
layout.attach(this._pimEntry, 0, 0, 1, 1);
@@ -355,24 +356,28 @@ var ShellMountPasswordDialog = GObject.registerClass({
this._passwordLabel = new St.Label({ style_class: 'prompt-dialog-password-label',
text: _("Password"),
y_align: Clutter.ActorAlign.CENTER });
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
can_focus: true,
x_expand: true });
this._passwordEntry = new St.PasswordEntry({
style_class: 'prompt-dialog-password-entry',
can_focus: true,
x_expand: true,
});
this._passwordEntry.clutter_text.connect('activate', this._onEntryActivate.bind(this));
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
ShellEntry.addContextMenu(this._passwordEntry);
this.setInitialKeyFocus(this._passwordEntry);
this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, {
animate: true,
});
this._passwordEntry.secondary_icon = this._workSpinner;
this._capsLockWarningLabel = new ShellEntry.CapsLockWarning();
if (rtl) {
layout.attach(this._passwordEntry, 0, 1, 1, 1);
layout.attach(this._passwordLabel, 1, 1, 1, 1);
layout.attach(this._capsLockWarningLabel, 0, 2, 1, 1);
} else {
layout.attach(this._passwordLabel, 0, 1, 1, 1);
layout.attach(this._passwordEntry, 1, 1, 1, 1);
layout.attach(this._capsLockWarningLabel, 1, 2, 1, 1);
}
content.messageBox.add(grid);

View File

@@ -41,7 +41,6 @@ var UnlockDialog = GObject.registerClass({
this._authPrompt.connect('failed', this._fail.bind(this));
this._authPrompt.connect('cancelled', this._fail.bind(this));
this._authPrompt.connect('reset', this._onReset.bind(this));
this._authPrompt.setPasswordChar('\u25cf');
this._authPrompt.nextButton.label = _("Unlock");
this._promptBox.add_child(this._authPrompt);

View File

@@ -132,20 +132,9 @@ else
have_systemd = false
endif
if get_option('extensions_tool')
autoar_dep = dependency('gnome-autoar-0')
json_dep = dependency('json-glib-1.0')
endif
bash_completion = dependency('bash-completion', required: false)
if get_option('man')
xsltproc = find_program('xsltproc')
if get_option('extensions_tool')
a2x = find_program('a2x')
endif
subdir('man')
endif
@@ -254,6 +243,16 @@ libgvc = subproject('gvc',
)
libgvc_gir = libgvc.get_variable('libgvc_gir')
if get_option('extensions_tool')
subproject('extensions-tool',
default_options: [
'man=@0@'.format(get_option('man')),
'package_name=@0@'.format(meson.project_name()),
]
)
endif
po_dir = join_paths(meson.current_source_dir(), 'po')
subdir('js')

View File

@@ -72,17 +72,6 @@ js/ui/windowAttentionHandler.js
js/ui/windowManager.js
js/ui/windowMenu.js
src/calendar-server/evolution-calendar.desktop.in
src/extensions-tool/command-create.c
src/extensions-tool/command-disable.c
src/extensions-tool/command-enable.c
src/extensions-tool/command-info.c
src/extensions-tool/command-install.c
src/extensions-tool/command-list.c
src/extensions-tool/command-pack.c
src/extensions-tool/command-prefs.c
src/extensions-tool/command-reset.c
src/extensions-tool/command-uninstall.c
src/extensions-tool/main.c
src/main.c
src/shell-app.c
src/shell-app-system.c
@@ -90,5 +79,16 @@ src/shell-global.c
src/shell-keyring-prompt.c
src/shell-polkit-authentication-agent.c
src/shell-util.c
subprojects/extensions-tool/src/command-create.c
subprojects/extensions-tool/src/command-disable.c
subprojects/extensions-tool/src/command-enable.c
subprojects/extensions-tool/src/command-info.c
subprojects/extensions-tool/src/command-install.c
subprojects/extensions-tool/src/command-list.c
subprojects/extensions-tool/src/command-pack.c
subprojects/extensions-tool/src/command-prefs.c
subprojects/extensions-tool/src/command-reset.c
subprojects/extensions-tool/src/command-uninstall.c
subprojects/extensions-tool/src/main.c
# Please do not remove this file from POTFILES.in. Run "git submodule init && git submodule update" to get it.
subprojects/gvc/gvc-mixer-control.c

View File

@@ -1,24 +0,0 @@
sources = [
'commands.h',
'command-create.c',
'command-disable.c',
'command-enable.c',
'command-info.c',
'command-install.c',
'command-list.c',
'command-pack.c',
'command-prefs.c',
'common.h',
'main.c'
]
resources = gnome.compile_resources('resources',
'gnome-extensions-tool.gresource.xml',
source_dir: '.'
)
executable('gnome-extensions',
sources, resources,
dependencies: [gio_dep, gio_unix_dep, autoar_dep, json_dep],
install: true
)

View File

@@ -6,10 +6,6 @@ subdir('hotplug-sniffer')
subdir('st')
subdir('tray')
if get_option('extensions_tool')
subdir('extensions-tool')
endif
script_data = configuration_data()
script_data.set('bindir', bindir)
script_data.set('datadir', datadir)

View File

@@ -15,6 +15,7 @@ st_headers = [
'st-icon-colors.h',
'st-image-content.h',
'st-label.h',
'st-password-entry.h',
'st-scrollable.h',
'st-scroll-bar.h',
'st-scroll-view.h',
@@ -125,6 +126,7 @@ st_sources = [
'st-icon-colors.c',
'st-image-content.c',
'st-label.c',
'st-password-entry.c',
'st-private.c',
'st-scrollable.c',
'st-scroll-bar.c',
@@ -153,14 +155,15 @@ st_gir_sources = st_sources + st_headers + st_enums
data_to_c = find_program(meson.source_root() + '/src/data-to-c.pl')
custom_target('scroll-view-fade-glsl',
glsl_sources = custom_target('scroll-view-fade-glsl',
input: ['st-scroll-view-fade.glsl'],
output: ['st-scroll-view-fade-generated.c'],
build_by_default: true,
output: ['st-scroll-view-fade-generated.h'],
capture: true,
command: [data_to_c, '@INPUT@', 'st_scroll_view_fade_glsl']
)
st_nogir_sources = [glsl_sources]
st_cflags = [
'-I@0@/src'.format(meson.source_root()),
'-I@0@'.format(meson.build_root()),
@@ -175,7 +178,7 @@ st_cflags = [
# Currently meson requires a shared library for building girs
libst = shared_library('st-1.0',
sources: st_gir_sources + croco_sources,
sources: st_gir_sources + st_nogir_sources + croco_sources,
c_args: st_cflags,
dependencies: [clutter_dep, gtk_dep, mutter_dep, libxml_dep, m_dep],
build_rpath: mutter_typelibdir,

View File

@@ -110,7 +110,6 @@ struct _StEntryPrivate
gfloat spacing;
gboolean capslock_warning_shown;
gboolean has_ibeam;
CoglPipeline *text_shadow_material;
@@ -216,61 +215,14 @@ st_entry_get_property (GObject *gobject,
}
}
static void
show_capslock_feedback (StEntry *entry)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (entry);
if (priv->secondary_icon == NULL)
{
ClutterActor *icon = g_object_new (ST_TYPE_ICON,
"style-class", "capslock-warning",
"icon-name", "dialog-warning-symbolic",
NULL);
st_entry_set_secondary_icon (entry, icon);
priv->capslock_warning_shown = TRUE;
}
}
static void
remove_capslock_feedback (StEntry *entry)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (entry);
if (priv->capslock_warning_shown)
{
st_entry_set_secondary_icon (entry, NULL);
priv->capslock_warning_shown = FALSE;
}
}
static void
keymap_state_changed (ClutterKeymap *keymap,
gpointer user_data)
{
StEntry *entry = ST_ENTRY (user_data);
StEntryPrivate *priv = ST_ENTRY_PRIV (entry);
if (clutter_text_get_password_char (CLUTTER_TEXT (priv->entry)) != 0)
{
if (clutter_keymap_get_caps_lock_state (keymap))
show_capslock_feedback (entry);
else
remove_capslock_feedback (entry);
}
}
static void
st_entry_dispose (GObject *object)
{
StEntry *entry = ST_ENTRY (object);
StEntryPrivate *priv = ST_ENTRY_PRIV (entry);
ClutterKeymap *keymap;
cogl_clear_object (&priv->text_shadow_material);
keymap = clutter_backend_get_keymap (clutter_get_default_backend ());
g_signal_handlers_disconnect_by_func (keymap, keymap_state_changed, entry);
G_OBJECT_CLASS (st_entry_parent_class)->dispose (object);
}
@@ -568,15 +520,9 @@ clutter_text_focus_in_cb (ClutterText *text,
ClutterActor *actor)
{
StEntry *entry = ST_ENTRY (actor);
ClutterKeymap *keymap;
st_entry_update_hint_visibility (entry);
keymap = clutter_backend_get_keymap (clutter_get_default_backend ());
keymap_state_changed (keymap, entry);
g_signal_connect (keymap, "state-changed",
G_CALLBACK (keymap_state_changed), entry);
st_widget_add_style_pseudo_class (ST_WIDGET (actor), "focus");
clutter_text_set_cursor_visible (text, TRUE);
}
@@ -586,29 +532,12 @@ clutter_text_focus_out_cb (ClutterText *text,
ClutterActor *actor)
{
StEntry *entry = ST_ENTRY (actor);
ClutterKeymap *keymap;
st_widget_remove_style_pseudo_class (ST_WIDGET (actor), "focus");
st_entry_update_hint_visibility (entry);
clutter_text_set_cursor_visible (text, FALSE);
remove_capslock_feedback (entry);
keymap = clutter_backend_get_keymap (clutter_get_default_backend ());
g_signal_handlers_disconnect_by_func (keymap, keymap_state_changed, entry);
}
static void
clutter_text_password_char_cb (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
StEntry *entry = ST_ENTRY (user_data);
StEntryPrivate *priv = ST_ENTRY_PRIV (entry);
if (clutter_text_get_password_char (CLUTTER_TEXT (priv->entry)) == 0)
remove_capslock_feedback (entry);
}
static void
@@ -1054,9 +983,6 @@ st_entry_init (StEntry *entry)
g_signal_connect (priv->entry, "key-focus-out",
G_CALLBACK (clutter_text_focus_out_cb), entry);
g_signal_connect (priv->entry, "notify::password-char",
G_CALLBACK (clutter_text_password_char_cb), entry);
g_signal_connect (priv->entry, "button-press-event",
G_CALLBACK (clutter_text_button_press_event), entry);

290
src/st/st-password-entry.c Normal file
View File

@@ -0,0 +1,290 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-password-entry.c: Password entry actor based on st-entry
*
* Copyright 2019 Endless Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "st-private.h"
#include "st-password-entry.h"
#include "st-icon.h"
#define BLACK_CIRCLE 9679
#define ST_PASSWORD_ENTRY_PRIV(x) st_password_entry_get_instance_private ((StPasswordEntry *) x)
typedef struct _StPasswordEntryPrivate StPasswordEntryPrivate;
struct _StPasswordEntry
{
/*< private >*/
StEntry parent_instance;
};
struct _StPasswordEntryPrivate
{
ClutterActor *peek_password_icon;
gboolean password_visible;
gboolean show_peek_icon;
};
enum
{
PROP_0,
PROP_PASSWORD_VISIBLE,
PROP_SHOW_PEEK_ICON,
N_PROPS
};
static GParamSpec *props[N_PROPS] = { NULL, };
G_DEFINE_TYPE_WITH_PRIVATE (StPasswordEntry, st_password_entry, ST_TYPE_ENTRY);
static void
st_password_entry_secondary_icon_clicked (StEntry *entry)
{
StPasswordEntry *password_entry = ST_PASSWORD_ENTRY (entry);
StPasswordEntryPrivate *priv = ST_PASSWORD_ENTRY_PRIV (password_entry);
st_password_entry_set_password_visible (password_entry, !priv->password_visible);
}
static void
st_password_entry_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StPasswordEntryPrivate *priv = ST_PASSWORD_ENTRY_PRIV (gobject);
switch (prop_id)
{
case PROP_PASSWORD_VISIBLE:
g_value_set_boolean (value, priv->password_visible);
break;
case PROP_SHOW_PEEK_ICON:
g_value_set_boolean (value, priv->show_peek_icon);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_password_entry_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StPasswordEntry *entry = ST_PASSWORD_ENTRY (gobject);
switch (prop_id)
{
case PROP_PASSWORD_VISIBLE:
st_password_entry_set_password_visible (entry, g_value_get_boolean (value));
break;
case PROP_SHOW_PEEK_ICON:
st_password_entry_set_show_peek_icon (entry, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_password_entry_class_init (StPasswordEntryClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
StEntryClass *st_entry_class = ST_ENTRY_CLASS (klass);
gobject_class->get_property = st_password_entry_get_property;
gobject_class->set_property = st_password_entry_set_property;
st_entry_class->secondary_icon_clicked = st_password_entry_secondary_icon_clicked;
props[PROP_PASSWORD_VISIBLE] = g_param_spec_boolean ("password-visible",
"Password visible",
"Whether to text in the entry is masked or not",
FALSE,
ST_PARAM_READWRITE);
props[PROP_SHOW_PEEK_ICON] = g_param_spec_boolean ("show-peek-icon",
"Show peek icon",
"Whether to show the password peek icon",
TRUE,
ST_PARAM_READWRITE);
g_object_class_install_properties (gobject_class, N_PROPS, props);
}
static void
clutter_text_password_char_cb (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
StPasswordEntry *entry = ST_PASSWORD_ENTRY (user_data);
ClutterActor *clutter_text;
clutter_text = st_entry_get_clutter_text (ST_ENTRY (entry));
if (clutter_text_get_password_char (CLUTTER_TEXT (clutter_text)) == 0)
st_password_entry_set_password_visible (entry, TRUE);
else
st_password_entry_set_password_visible (entry, FALSE);
}
static void
st_password_entry_init (StPasswordEntry *entry)
{
StPasswordEntryPrivate *priv = ST_PASSWORD_ENTRY_PRIV (entry);
ClutterActor *clutter_text;
priv->peek_password_icon = g_object_new (ST_TYPE_ICON,
"style-class", "peek-password",
"icon-name", "eye-not-looking-symbolic",
NULL);
st_entry_set_secondary_icon (ST_ENTRY (entry), priv->peek_password_icon);
priv->peek_password_icon = g_object_new (ST_TYPE_ICON,
"style-class", "peek-password",
"icon-name", "eye-not-looking-symbolic",
NULL);
st_entry_set_secondary_icon (ST_ENTRY (entry), priv->peek_password_icon);
clutter_text = st_entry_get_clutter_text (ST_ENTRY (entry));
clutter_text_set_password_char (CLUTTER_TEXT (clutter_text), BLACK_CIRCLE);
st_entry_set_input_purpose (ST_ENTRY (entry), CLUTTER_INPUT_CONTENT_PURPOSE_PASSWORD);
g_signal_connect (clutter_text, "notify::password-char",
G_CALLBACK (clutter_text_password_char_cb), entry);
}
/**
* st_password_entry_new:
*
* Create a new #StPasswordEntry.
*
* Returns: a new #StEntry
*/
StEntry*
st_password_entry_new (void)
{
return ST_ENTRY (g_object_new (ST_TYPE_PASSWORD_ENTRY, NULL));
}
/**
* st_password_entry_set_show_peek_icon:
* @entry: a #StPasswordEntry
* @value: #TRUE to show the peek-icon in the entry, #FALSE otherwise
*
* Sets whether to show or hide the peek-icon in the password entry.
*/
void
st_password_entry_set_show_peek_icon (StPasswordEntry *entry, gboolean value)
{
StPasswordEntryPrivate *priv;
g_return_if_fail (ST_IS_PASSWORD_ENTRY (entry));
priv = ST_PASSWORD_ENTRY_PRIV (entry);
if (priv->show_peek_icon == value)
return;
priv->show_peek_icon = value;
if (priv->show_peek_icon)
st_entry_set_secondary_icon (ST_ENTRY (entry), priv->peek_password_icon);
else
st_entry_set_secondary_icon (ST_ENTRY (entry), NULL);
g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_SHOW_PEEK_ICON]);
}
/**
* st_password_entry_get_show_peek_icon:
* @entry: a #StPasswordEntry
*
* Gets whether peek-icon is shown or hidden in the password entry.
*/
gboolean
st_password_entry_get_show_peek_icon (StPasswordEntry *entry)
{
StPasswordEntryPrivate *priv;
g_return_val_if_fail (ST_IS_PASSWORD_ENTRY (entry), TRUE);
priv = ST_PASSWORD_ENTRY_PRIV (entry);
return priv->show_peek_icon;
}
/**
* st_password_entry_set_password_visible:
* @entry: a #StPasswordEntry
* @value: #TRUE to show the password in the entry, #FALSE otherwise
*
* Sets whether to show or hide text in the password entry.
*/
void
st_password_entry_set_password_visible (StPasswordEntry *entry, gboolean value)
{
StPasswordEntryPrivate *priv;
ClutterActor *clutter_text;
g_return_if_fail (ST_IS_PASSWORD_ENTRY (entry));
priv = ST_PASSWORD_ENTRY_PRIV (entry);
if (priv->password_visible == value)
return;
priv->password_visible = value;
clutter_text = st_entry_get_clutter_text (ST_ENTRY (entry));
if (priv->password_visible)
{
clutter_text_set_password_char (CLUTTER_TEXT (clutter_text), 0);
st_icon_set_icon_name (ST_ICON (priv->peek_password_icon), "eye-open-negative-filled-symbolic");
}
else
{
clutter_text_set_password_char (CLUTTER_TEXT (clutter_text), BLACK_CIRCLE);
st_icon_set_icon_name (ST_ICON (priv->peek_password_icon), "eye-not-looking-symbolic");
}
g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_PASSWORD_VISIBLE]);
}
/**
* st_password_entry_get_password_visible:
* @entry: a #StPasswordEntry
*
* Gets whether the text is masked in the password entry.
*/
gboolean
st_password_entry_get_password_visible (StPasswordEntry *entry)
{
StPasswordEntryPrivate *priv;
g_return_val_if_fail (ST_IS_PASSWORD_ENTRY (entry), FALSE);
priv = ST_PASSWORD_ENTRY_PRIV (entry);
return priv->password_visible;
}

View File

@@ -0,0 +1,46 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-password-entry.h: Password entry actor based on st-entry
*
* Copyright 2019 Endless Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_PASSWORD_ENTRY_H__
#define __ST_PASSWORD_ENTRY_H__
G_BEGIN_DECLS
#include <st/st-entry.h>
#define ST_TYPE_PASSWORD_ENTRY (st_password_entry_get_type ())
G_DECLARE_FINAL_TYPE (StPasswordEntry, st_password_entry, ST, PASSWORD_ENTRY, StEntry)
StEntry *st_password_entry_new (void);
gboolean st_password_entry_get_password_visible (StPasswordEntry *entry);
void st_password_entry_set_password_visible (StPasswordEntry *entry,
gboolean value);
gboolean st_password_entry_get_show_peek_icon (StPasswordEntry *entry);
void st_password_entry_set_show_peek_icon (StPasswordEntry *entry,
gboolean value);
G_END_DECLS
#endif /* __ST_PASSWORD_ENTRY_H__ */

View File

@@ -32,7 +32,7 @@
#define DEFAULT_FADE_OFFSET 68.0f
#include "st-scroll-view-fade-generated.c"
#include "st-scroll-view-fade-generated.h"
struct _StScrollViewFade
{

View File

@@ -0,0 +1,23 @@
# gnome-extensions-tool
gnome-extensions-tool is a command line utility for managing
GNOME Shell extensions. It is usually built as part of gnome-shell,
but can be used as a stand-alone project as well (for example to
create an extension bundle as part of continuous integration).
Bugs should be reported to the GNOME [bug tracking system][bug-tracker].
## Building
Before the project can be built stand-alone, the po directory has
to be populated with translations (from gnome-shell).
To do that, simply run the included script:
```sh
$ ./generate-translations.sh
```
## License
gnome-extensions-tool is distributed under the terms of the GNU General Public
License, version 3 or later. See the [COPYING][license] file for details.
[bug-tracker]: https://gitlab.gnome.org/GNOME/gnome-shell/issues
[license]: COPYING

View File

@@ -0,0 +1,19 @@
#!/usr/bin/bash
cd $(dirname $0)
sed -e '/subprojects\/extensions-tool/!d' \
-e 's:subprojects/extensions-tool/::' ../../po/POTFILES.in > po/POTFILES.in
for l in $(<po/LINGUAS)
do
cp ../../po/$l.po po/$l.po
done
builddir=$(mktemp -d -p.)
meson -Dman=False $builddir
ninja -C $builddir gnome-extensions-tool-pot
ninja -C $builddir gnome-extensions-tool-update-po
rm -rf $builddir

View File

@@ -0,0 +1,46 @@
project('gnome-extensions-tool', 'c',
version: '3.35.1',
meson_version: '>= 0.47.0',
license: 'GPLv2+'
)
gio_req = '>= 2.56.0'
gnome = import('gnome')
i18n = import('i18n')
package_name = get_option('package_name')
prefix = get_option('prefix')
bindir = join_paths(prefix, get_option('bindir'))
datadir = join_paths(prefix, get_option('datadir'))
mandir = join_paths(prefix, get_option('mandir'))
localedir = join_paths(datadir, 'locale')
gio_dep = dependency('gio-2.0', version: gio_req)
gio_unix_dep = dependency('gio-unix-2.0', version: gio_req)
autoar_dep = dependency('gnome-autoar-0')
json_dep = dependency('json-glib-1.0')
cc = meson.get_compiler('c')
bash_completion = dependency('bash-completion', required: false)
subdir('src')
if bash_completion.found()
install_data('completion/bash/gnome-extensions',
install_dir: bash_completion.get_pkgconfig_variable('completionsdir')
)
endif
if get_option('man')
a2x = find_program('a2x')
subdir('man')
endif
if not meson.is_subproject()
subdir('po')
endif

View File

@@ -0,0 +1,11 @@
option('man',
type: 'boolean',
value: true,
description: 'Generate man pages'
)
option('package_name',
type: 'string',
value: 'gnome-extensions-tool',
description: 'The gettext domain name'
)

View File

@@ -0,0 +1,3 @@
*.po
*.pot
POTFILES.in

View File

@@ -0,0 +1 @@
../../../po/LINGUAS

View File

@@ -0,0 +1 @@
i18n.gettext(package_name, preset: 'glib')

View File

@@ -118,9 +118,14 @@ launch_extension_source (GFile *dir, GError **error)
GList l;
main_source = g_file_get_child (dir, "extension.js");
handler = g_file_query_default_handler (main_source, NULL, error);
handler = g_file_query_default_handler (main_source, NULL, NULL);
if (handler == NULL)
return FALSE;
{
/* Translators: a file path to an extension directory */
g_print (_("The new extension was successfully created in %s.\n"),
g_file_peek_path (dir));
return TRUE;
}
l.data = main_source;
l.next = l.prev = NULL;

View File

@@ -1,5 +1,5 @@
config_h = configuration_data()
config_h.set_quoted('GETTEXT_PACKAGE', meson.project_name())
config_h.set_quoted('GETTEXT_PACKAGE', package_name)
config_h.set_quoted('VERSION', meson.project_version())
config_h.set_quoted('LOCALEDIR', localedir)
config_h.set('HAVE_BIND_TEXTDOMAIN_CODESET', cc.has_function('bind_textdomain_codeset'))
@@ -32,13 +32,3 @@ executable('gnome-extensions',
dependencies: [gio_dep, gio_unix_dep, autoar_dep, json_dep],
install: true
)
if bash_completion.found()
install_data('completion/bash/gnome-extensions',
install_dir: bash_completion.get_pkgconfig_variable('completionsdir')
)
endif
if get_option('man')
subdir('man')
endif