9d941f8202
xgettext gained some support for template strings, and no longer fails when encountering '/' somewhere between backticks. Unfortunately its support is still buggy as hell, and it is now silently dropping translatable strings, yay. I hate making the code worse, but until xgettext really gets its shit together, the only viable way forward seems to be to not use template strings in any files listed in POTFILES. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1014
874 lines
27 KiB
JavaScript
874 lines
27 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
/* exported UnlockDialog */
|
|
|
|
const { AccountsService, Atk, Clutter, Gdm, Gio,
|
|
GnomeDesktop, GLib, GObject, Meta, Shell, St } = imports.gi;
|
|
|
|
const Background = imports.ui.background;
|
|
const Layout = imports.ui.layout;
|
|
const Main = imports.ui.main;
|
|
const MessageTray = imports.ui.messageTray;
|
|
const SwipeTracker = imports.ui.swipeTracker;
|
|
|
|
const AuthPrompt = imports.gdm.authPrompt;
|
|
|
|
// The timeout before going back automatically to the lock screen (in seconds)
|
|
const IDLE_TIMEOUT = 2 * 60;
|
|
|
|
// The timeout before showing the unlock hint (in seconds)
|
|
const HINT_TIMEOUT = 4;
|
|
|
|
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
|
|
|
const CROSSFADE_TIME = 300;
|
|
const FADE_OUT_TRANSLATION = 200;
|
|
const FADE_OUT_SCALE = 0.3;
|
|
|
|
const BLUR_BRIGHTNESS = 0.55;
|
|
const BLUR_SIGMA = 60;
|
|
|
|
const SUMMARY_ICON_SIZE = 32;
|
|
|
|
var NotificationsBox = GObject.registerClass({
|
|
Signals: { 'wake-up-screen': {} },
|
|
}, class NotificationsBox extends St.BoxLayout {
|
|
_init() {
|
|
super._init({
|
|
vertical: true,
|
|
name: 'unlockDialogNotifications',
|
|
style_class: 'unlock-dialog-notifications-container',
|
|
});
|
|
|
|
this._scrollView = new St.ScrollView({ hscrollbar_policy: St.PolicyType.NEVER });
|
|
this._notificationBox = new St.BoxLayout({
|
|
vertical: true,
|
|
style_class: 'unlock-dialog-notifications-container',
|
|
});
|
|
this._scrollView.add_actor(this._notificationBox);
|
|
|
|
this.add_child(this._scrollView);
|
|
|
|
this._sources = new Map();
|
|
Main.messageTray.getSources().forEach(source => {
|
|
this._sourceAdded(Main.messageTray, source, true);
|
|
});
|
|
this._updateVisibility();
|
|
|
|
this._sourceAddedId = Main.messageTray.connect('source-added', this._sourceAdded.bind(this));
|
|
|
|
this.connect('destroy', this._onDestroy.bind(this));
|
|
}
|
|
|
|
_onDestroy() {
|
|
if (this._sourceAddedId) {
|
|
Main.messageTray.disconnect(this._sourceAddedId);
|
|
this._sourceAddedId = 0;
|
|
}
|
|
|
|
let items = this._sources.entries();
|
|
for (let [source, obj] of items)
|
|
this._removeSource(source, obj);
|
|
}
|
|
|
|
_updateVisibility() {
|
|
this._notificationBox.visible =
|
|
this._notificationBox.get_children().some(a => a.visible);
|
|
|
|
this.visible = this._notificationBox.visible;
|
|
}
|
|
|
|
_makeNotificationSource(source, box) {
|
|
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
|
|
box.add_child(sourceActor);
|
|
|
|
let textBox = new St.BoxLayout({
|
|
x_expand: true,
|
|
y_expand: true,
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
});
|
|
box.add_child(textBox);
|
|
|
|
let title = new St.Label({
|
|
text: source.title,
|
|
style_class: 'unlock-dialog-notification-label',
|
|
x_expand: true,
|
|
x_align: Clutter.ActorAlign.START,
|
|
});
|
|
textBox.add(title);
|
|
|
|
let count = source.unseenCount;
|
|
let countLabel = new St.Label({
|
|
text: count.toString(),
|
|
visible: count > 1,
|
|
style_class: 'unlock-dialog-notification-count-text',
|
|
});
|
|
textBox.add(countLabel);
|
|
|
|
box.visible = count !== 0;
|
|
return [title, countLabel];
|
|
}
|
|
|
|
_makeNotificationDetailedSource(source, box) {
|
|
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
|
|
let sourceBin = new St.Bin({ child: sourceActor });
|
|
box.add(sourceBin);
|
|
|
|
let textBox = new St.BoxLayout({ vertical: true });
|
|
box.add_child(textBox);
|
|
|
|
let title = new St.Label({
|
|
text: source.title,
|
|
style_class: 'unlock-dialog-notification-label',
|
|
});
|
|
textBox.add(title);
|
|
|
|
let visible = false;
|
|
for (let i = 0; i < source.notifications.length; i++) {
|
|
let n = source.notifications[i];
|
|
|
|
if (n.acknowledged)
|
|
continue;
|
|
|
|
let body = '';
|
|
if (n.bannerBodyText) {
|
|
body = n.bannerBodyMarkup
|
|
? n.bannerBodyText
|
|
: GLib.markup_escape_text(n.bannerBodyText, -1);
|
|
}
|
|
|
|
let label = new St.Label({ style_class: 'unlock-dialog-notification-count-text' });
|
|
label.clutter_text.set_markup('<b>%s</b> %s'.format(n.title, body));
|
|
textBox.add(label);
|
|
|
|
visible = true;
|
|
}
|
|
|
|
box.visible = visible;
|
|
return [title, null];
|
|
}
|
|
|
|
_shouldShowDetails(source) {
|
|
return source.policy.detailsInLockScreen ||
|
|
source.narrowestPrivacyScope === MessageTray.PrivacyScope.SYSTEM;
|
|
}
|
|
|
|
_updateSourceBoxStyle(source, obj, box) {
|
|
let hasCriticalNotification =
|
|
source.notifications.some(n => n.urgency === MessageTray.Urgency.CRITICAL);
|
|
|
|
if (hasCriticalNotification !== obj.hasCriticalNotification) {
|
|
obj.hasCriticalNotification = hasCriticalNotification;
|
|
|
|
if (hasCriticalNotification)
|
|
box.add_style_class_name('critical');
|
|
else
|
|
box.remove_style_class_name('critical');
|
|
}
|
|
}
|
|
|
|
_showSource(source, obj, box) {
|
|
if (obj.detailed)
|
|
[obj.titleLabel, obj.countLabel] = this._makeNotificationDetailedSource(source, box);
|
|
else
|
|
[obj.titleLabel, obj.countLabel] = this._makeNotificationSource(source, box);
|
|
|
|
box.visible = obj.visible && (source.unseenCount > 0);
|
|
|
|
this._updateSourceBoxStyle(source, obj, box);
|
|
}
|
|
|
|
_sourceAdded(tray, source, initial) {
|
|
let obj = {
|
|
visible: source.policy.showInLockScreen,
|
|
detailed: this._shouldShowDetails(source),
|
|
sourceDestroyId: 0,
|
|
sourceCountChangedId: 0,
|
|
sourceTitleChangedId: 0,
|
|
sourceUpdatedId: 0,
|
|
sourceBox: null,
|
|
titleLabel: null,
|
|
countLabel: null,
|
|
hasCriticalNotification: false,
|
|
};
|
|
|
|
obj.sourceBox = new St.BoxLayout({
|
|
style_class: 'unlock-dialog-notification-source',
|
|
x_expand: true,
|
|
});
|
|
this._showSource(source, obj, obj.sourceBox);
|
|
this._notificationBox.add_child(obj.sourceBox);
|
|
|
|
obj.sourceCountChangedId = source.connect('notify::count', () => {
|
|
this._countChanged(source, obj);
|
|
});
|
|
obj.sourceTitleChangedId = source.connect('notify::title', () => {
|
|
this._titleChanged(source, obj);
|
|
});
|
|
obj.policyChangedId = source.policy.connect('notify', (policy, pspec) => {
|
|
if (pspec.name === 'show-in-lock-screen')
|
|
this._visibleChanged(source, obj);
|
|
else
|
|
this._detailedChanged(source, obj);
|
|
});
|
|
obj.sourceDestroyId = source.connect('destroy', () => {
|
|
this._onSourceDestroy(source, obj);
|
|
});
|
|
|
|
this._sources.set(source, obj);
|
|
|
|
if (!initial) {
|
|
// block scrollbars while animating, if they're not needed now
|
|
let boxHeight = this._notificationBox.height;
|
|
if (this._scrollView.height >= boxHeight)
|
|
this._scrollView.vscrollbar_policy = St.PolicyType.NEVER;
|
|
|
|
let widget = obj.sourceBox;
|
|
let [, natHeight] = widget.get_preferred_height(-1);
|
|
widget.height = 0;
|
|
widget.ease({
|
|
height: natHeight,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
duration: 250,
|
|
onComplete: () => {
|
|
this._scrollView.vscrollbar_policy = St.PolicyType.AUTOMATIC;
|
|
widget.set_height(-1);
|
|
},
|
|
});
|
|
|
|
this._updateVisibility();
|
|
if (obj.sourceBox.visible)
|
|
this.emit('wake-up-screen');
|
|
}
|
|
}
|
|
|
|
_titleChanged(source, obj) {
|
|
obj.titleLabel.text = source.title;
|
|
}
|
|
|
|
_countChanged(source, obj) {
|
|
// A change in the number of notifications may change whether we show
|
|
// details.
|
|
let newDetailed = this._shouldShowDetails(source);
|
|
let oldDetailed = obj.detailed;
|
|
|
|
obj.detailed = newDetailed;
|
|
|
|
if (obj.detailed || oldDetailed !== newDetailed) {
|
|
// A new notification was pushed, or a previous notification was destroyed.
|
|
// Give up, and build the list again.
|
|
|
|
obj.sourceBox.destroy_all_children();
|
|
obj.titleLabel = obj.countLabel = null;
|
|
this._showSource(source, obj, obj.sourceBox);
|
|
} else {
|
|
let count = source.unseenCount;
|
|
obj.countLabel.text = count.toString();
|
|
obj.countLabel.visible = count > 1;
|
|
}
|
|
|
|
obj.sourceBox.visible = obj.visible && (source.unseenCount > 0);
|
|
|
|
this._updateVisibility();
|
|
if (obj.sourceBox.visible)
|
|
this.emit('wake-up-screen');
|
|
}
|
|
|
|
_visibleChanged(source, obj) {
|
|
if (obj.visible === source.policy.showInLockScreen)
|
|
return;
|
|
|
|
obj.visible = source.policy.showInLockScreen;
|
|
obj.sourceBox.visible = obj.visible && source.unseenCount > 0;
|
|
|
|
this._updateVisibility();
|
|
if (obj.sourceBox.visible)
|
|
this.emit('wake-up-screen');
|
|
}
|
|
|
|
_detailedChanged(source, obj) {
|
|
let newDetailed = this._shouldShowDetails(source);
|
|
if (obj.detailed === newDetailed)
|
|
return;
|
|
|
|
obj.detailed = newDetailed;
|
|
|
|
obj.sourceBox.destroy_all_children();
|
|
obj.titleLabel = obj.countLabel = null;
|
|
this._showSource(source, obj, obj.sourceBox);
|
|
}
|
|
|
|
_onSourceDestroy(source, obj) {
|
|
this._removeSource(source, obj);
|
|
this._updateVisibility();
|
|
}
|
|
|
|
_removeSource(source, obj) {
|
|
obj.sourceBox.destroy();
|
|
obj.sourceBox = obj.titleLabel = obj.countLabel = null;
|
|
|
|
source.disconnect(obj.sourceDestroyId);
|
|
source.disconnect(obj.sourceCountChangedId);
|
|
source.disconnect(obj.sourceTitleChangedId);
|
|
source.policy.disconnect(obj.policyChangedId);
|
|
|
|
this._sources.delete(source);
|
|
}
|
|
});
|
|
|
|
var Clock = GObject.registerClass(
|
|
class UnlockDialogClock extends St.BoxLayout {
|
|
_init() {
|
|
super._init({ style_class: 'unlock-dialog-clock', vertical: true });
|
|
|
|
this._time = new St.Label({
|
|
style_class: 'unlock-dialog-clock-time',
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
});
|
|
this._date = new St.Label({
|
|
style_class: 'unlock-dialog-clock-date',
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
});
|
|
this._hint = new St.Label({
|
|
style_class: 'unlock-dialog-clock-hint',
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
opacity: 0,
|
|
});
|
|
|
|
this.add_child(this._time);
|
|
this.add_child(this._date);
|
|
this.add_child(this._hint);
|
|
|
|
this._wallClock = new GnomeDesktop.WallClock({ time_only: true });
|
|
this._wallClock.connect('notify::clock', this._updateClock.bind(this));
|
|
|
|
this._seat = Clutter.get_default_backend().get_default_seat();
|
|
this._touchModeChangedId = this._seat.connect('notify::touch-mode',
|
|
this._updateHint.bind(this));
|
|
|
|
this._monitorManager = Meta.MonitorManager.get();
|
|
this._powerModeChangedId = this._monitorManager.connect(
|
|
'power-save-mode-changed', () => (this._hint.opacity = 0));
|
|
|
|
this._idleMonitor = Meta.IdleMonitor.get_core();
|
|
this._idleWatchId = this._idleMonitor.add_idle_watch(HINT_TIMEOUT * 1000, () => {
|
|
this._hint.ease({
|
|
opacity: 255,
|
|
duration: CROSSFADE_TIME,
|
|
});
|
|
});
|
|
|
|
this._updateClock();
|
|
this._updateHint();
|
|
|
|
this.connect('destroy', this._onDestroy.bind(this));
|
|
}
|
|
|
|
_updateClock() {
|
|
this._time.text = this._wallClock.clock;
|
|
|
|
let date = new Date();
|
|
/* Translators: This is a time format for a date in
|
|
long format */
|
|
let dateFormat = Shell.util_translate_time_string(N_('%A %B %-d'));
|
|
this._date.text = date.toLocaleFormat(dateFormat);
|
|
}
|
|
|
|
_updateHint() {
|
|
this._hint.text = this._seat.touch_mode
|
|
? _('Swipe up to unlock')
|
|
: _('Click or press a key to unlock');
|
|
}
|
|
|
|
_onDestroy() {
|
|
this._wallClock.run_dispose();
|
|
|
|
this._seat.disconnect(this._touchModeChangedId);
|
|
this._idleMonitor.remove_watch(this._idleWatchId);
|
|
this._monitorManager.disconnect(this._powerModeChangedId);
|
|
}
|
|
});
|
|
|
|
var UnlockDialogLayout = GObject.registerClass(
|
|
class UnlockDialogLayout extends Clutter.LayoutManager {
|
|
_init(stack, notifications, switchUserButton) {
|
|
super._init();
|
|
|
|
this._stack = stack;
|
|
this._notifications = notifications;
|
|
this._switchUserButton = switchUserButton;
|
|
}
|
|
|
|
vfunc_get_preferred_width(container, forHeight) {
|
|
return this._stack.get_preferred_width(forHeight);
|
|
}
|
|
|
|
vfunc_get_preferred_height(container, forWidth) {
|
|
return this._stack.get_preferred_height(forWidth);
|
|
}
|
|
|
|
vfunc_allocate(container, box, flags) {
|
|
let [width, height] = box.get_size();
|
|
|
|
let tenthOfHeight = height / 10.0;
|
|
let thirdOfHeight = height / 3.0;
|
|
|
|
let [, , stackWidth, stackHeight] =
|
|
this._stack.get_preferred_size();
|
|
|
|
let [, , notificationsWidth, notificationsHeight] =
|
|
this._notifications.get_preferred_size();
|
|
|
|
let columnWidth = Math.max(stackWidth, notificationsWidth);
|
|
|
|
let columnX1 = Math.floor((width - columnWidth) / 2.0);
|
|
let actorBox = new Clutter.ActorBox();
|
|
|
|
// Notifications
|
|
let maxNotificationsHeight = Math.min(
|
|
notificationsHeight,
|
|
height - tenthOfHeight - stackHeight);
|
|
|
|
actorBox.x1 = columnX1;
|
|
actorBox.y1 = height - maxNotificationsHeight;
|
|
actorBox.x2 = columnX1 + columnWidth;
|
|
actorBox.y2 = actorBox.y1 + maxNotificationsHeight;
|
|
|
|
this._notifications.allocate(actorBox, flags);
|
|
|
|
// Authentication Box
|
|
let stackY = Math.min(
|
|
thirdOfHeight,
|
|
height - stackHeight - maxNotificationsHeight);
|
|
|
|
actorBox.x1 = columnX1;
|
|
actorBox.y1 = stackY;
|
|
actorBox.x2 = columnX1 + columnWidth;
|
|
actorBox.y2 = stackY + stackHeight;
|
|
|
|
this._stack.allocate(actorBox, flags);
|
|
|
|
// Switch User button
|
|
if (this._switchUserButton.visible) {
|
|
let [, , natWidth, natHeight] =
|
|
this._switchUserButton.get_preferred_size();
|
|
|
|
const textDirection = this._switchUserButton.get_text_direction();
|
|
if (textDirection === Clutter.TextDirection.RTL)
|
|
actorBox.x1 = box.x1 + natWidth;
|
|
else
|
|
actorBox.x1 = box.x2 - (natWidth * 2);
|
|
|
|
actorBox.y1 = box.y2 - (natHeight * 2);
|
|
actorBox.x2 = actorBox.x1 + natWidth;
|
|
actorBox.y2 = actorBox.y1 + natHeight;
|
|
|
|
this._switchUserButton.allocate(actorBox, flags);
|
|
}
|
|
}
|
|
});
|
|
|
|
var UnlockDialog = GObject.registerClass({
|
|
Signals: {
|
|
'failed': {},
|
|
'wake-up-screen': {},
|
|
},
|
|
}, class UnlockDialog extends St.Widget {
|
|
_init(parentActor) {
|
|
super._init({
|
|
accessible_role: Atk.Role.WINDOW,
|
|
style_class: 'login-dialog',
|
|
visible: false,
|
|
can_focus: true,
|
|
reactive: true,
|
|
});
|
|
|
|
parentActor.add_child(this);
|
|
|
|
this._gdmClient = new Gdm.Client();
|
|
|
|
this._adjustment = new St.Adjustment({
|
|
lower: 0,
|
|
upper: 2,
|
|
page_size: 1,
|
|
page_increment: 1,
|
|
});
|
|
this._adjustment.connect('notify::value', () => {
|
|
this._setTransitionProgress(this._adjustment.value);
|
|
});
|
|
|
|
this._swipeTracker = new SwipeTracker.SwipeTracker(
|
|
this, Shell.ActionMode.UNLOCK_SCREEN);
|
|
this._swipeTracker.connect('begin', this._swipeBegin.bind(this));
|
|
this._swipeTracker.connect('update', this._swipeUpdate.bind(this));
|
|
this._swipeTracker.connect('end', this._swipeEnd.bind(this));
|
|
|
|
this.connect('scroll-event', (o, event) => {
|
|
if (this._swipeTracker.canHandleScrollEvent(event))
|
|
return Clutter.EVENT_PROPAGATE;
|
|
|
|
let direction = event.get_scroll_direction();
|
|
if (direction === Clutter.ScrollDirection.UP)
|
|
this._showClock();
|
|
else if (direction === Clutter.ScrollDirection.DOWN)
|
|
this._showPrompt();
|
|
return Clutter.EVENT_STOP;
|
|
});
|
|
|
|
this._activePage = null;
|
|
|
|
let tapAction = new Clutter.TapAction();
|
|
tapAction.connect('tap', this._showPrompt.bind(this));
|
|
this.add_action(tapAction);
|
|
|
|
// Background
|
|
this._backgroundGroup = new Clutter.Actor();
|
|
this.add_child(this._backgroundGroup);
|
|
|
|
this._bgManagers = [];
|
|
|
|
this._updateBackgrounds();
|
|
this._monitorsChangedId =
|
|
Main.layoutManager.connect('monitors-changed', this._updateBackgrounds.bind(this));
|
|
|
|
this._userManager = AccountsService.UserManager.get_default();
|
|
this._userName = GLib.get_user_name();
|
|
this._user = this._userManager.get_user(this._userName);
|
|
|
|
// Authentication & Clock stack
|
|
this._stack = new Shell.Stack();
|
|
|
|
this._promptBox = new St.BoxLayout({ vertical: true });
|
|
this._promptBox.set_pivot_point(0.5, 0.5);
|
|
this._promptBox.hide();
|
|
this._stack.add_child(this._promptBox);
|
|
|
|
this._clock = new Clock();
|
|
this._clock.set_pivot_point(0.5, 0.5);
|
|
this._stack.add_child(this._clock);
|
|
this._showClock();
|
|
|
|
this.allowCancel = false;
|
|
|
|
Main.ctrlAltTabManager.addGroup(this, _('Unlock Window'), 'dialog-password-symbolic');
|
|
|
|
// Notifications
|
|
this._notificationsBox = new NotificationsBox();
|
|
this._notificationsBox.connect('wake-up-screen', () => this.emit('wake-up-screen'));
|
|
|
|
// Switch User button
|
|
this._otherUserButton = new St.Button({
|
|
style_class: 'modal-dialog-button button switch-user-button',
|
|
can_focus: true,
|
|
reactive: true,
|
|
x_align: Clutter.ActorAlign.END,
|
|
y_align: Clutter.ActorAlign.END,
|
|
child: new St.Icon({ icon_name: 'system-users-symbolic' }),
|
|
});
|
|
this._otherUserButton.connect('clicked', this._otherUserClicked.bind(this));
|
|
|
|
let screenSaverSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.screensaver' });
|
|
screenSaverSettings.bind('user-switch-enabled',
|
|
this._otherUserButton, 'visible', Gio.SettingsBindFlags.GET);
|
|
|
|
// Main Box
|
|
let mainBox = new Clutter.Actor();
|
|
mainBox.add_constraint(new Layout.MonitorConstraint({ primary: true }));
|
|
mainBox.add_child(this._stack);
|
|
mainBox.add_child(this._notificationsBox);
|
|
mainBox.add_child(this._otherUserButton);
|
|
mainBox.layout_manager = new UnlockDialogLayout(
|
|
this._stack,
|
|
this._notificationsBox,
|
|
this._otherUserButton);
|
|
this.add_child(mainBox);
|
|
|
|
this._idleMonitor = Meta.IdleMonitor.get_core();
|
|
this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, this._escape.bind(this));
|
|
|
|
this.connect('destroy', this._onDestroy.bind(this));
|
|
}
|
|
|
|
vfunc_key_press_event(keyEvent) {
|
|
if (this._activePage === this._promptBox ||
|
|
(this._promptBox && this._promptBox.visible))
|
|
return Clutter.EVENT_PROPAGATE;
|
|
|
|
const { keyval } = keyEvent;
|
|
if (keyval === Clutter.KEY_Shift_L ||
|
|
keyval === Clutter.KEY_Shift_R ||
|
|
keyval === Clutter.KEY_Shift_Lock ||
|
|
keyval === Clutter.KEY_Caps_Lock)
|
|
return Clutter.EVENT_PROPAGATE;
|
|
|
|
let unichar = keyEvent.unicode_value;
|
|
|
|
this._showPrompt();
|
|
|
|
if (GLib.unichar_isgraph(unichar))
|
|
this.addCharacter(unichar);
|
|
|
|
return Clutter.EVENT_PROPAGATE;
|
|
}
|
|
|
|
_createBackground(monitorIndex) {
|
|
let monitor = Main.layoutManager.monitors[monitorIndex];
|
|
let widget = new St.Widget({
|
|
style_class: 'screen-shield-background',
|
|
x: monitor.x,
|
|
y: monitor.y,
|
|
width: monitor.width,
|
|
height: monitor.height,
|
|
});
|
|
|
|
let bgManager = new Background.BackgroundManager({
|
|
container: widget,
|
|
monitorIndex,
|
|
controlPosition: false,
|
|
settingsSchema: SCREENSAVER_SCHEMA,
|
|
});
|
|
|
|
this._bgManagers.push(bgManager);
|
|
|
|
this._backgroundGroup.add_child(widget);
|
|
|
|
const themeContext = St.ThemeContext.get_for_stage(global.stage);
|
|
|
|
let effect = new Shell.BlurEffect({
|
|
brightness: BLUR_BRIGHTNESS,
|
|
sigma: BLUR_SIGMA * themeContext.scale_factor,
|
|
});
|
|
|
|
this._scaleChangedId = themeContext.connect('notify::scale-factor', () => {
|
|
effect.sigma = BLUR_SIGMA * themeContext.scale_factor;
|
|
});
|
|
|
|
widget.add_effect(effect);
|
|
}
|
|
|
|
_updateBackgrounds() {
|
|
for (let i = 0; i < this._bgManagers.length; i++)
|
|
this._bgManagers[i].destroy();
|
|
|
|
this._bgManagers = [];
|
|
this._backgroundGroup.destroy_all_children();
|
|
|
|
for (let i = 0; i < Main.layoutManager.monitors.length; i++)
|
|
this._createBackground(i);
|
|
}
|
|
|
|
_ensureAuthPrompt() {
|
|
if (this._authPrompt)
|
|
return;
|
|
|
|
this._authPrompt = new AuthPrompt.AuthPrompt(this._gdmClient,
|
|
AuthPrompt.AuthPromptMode.UNLOCK_ONLY);
|
|
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._promptBox.add_child(this._authPrompt);
|
|
|
|
this._authPrompt.reset();
|
|
this._updateSensitivity(true);
|
|
}
|
|
|
|
_maybeDestroyAuthPrompt() {
|
|
let focus = global.stage.key_focus;
|
|
if (focus === null ||
|
|
(this._authPrompt && this._authPrompt.contains(focus)) ||
|
|
(this._otherUserButton && focus === this._otherUserButton))
|
|
this.grab_key_focus();
|
|
|
|
if (this._authPrompt) {
|
|
this._authPrompt.destroy();
|
|
this._authPrompt = null;
|
|
}
|
|
}
|
|
|
|
_updateSensitivity(sensitive) {
|
|
this._authPrompt.updateSensitivity(sensitive);
|
|
|
|
this._otherUserButton.reactive = sensitive;
|
|
this._otherUserButton.can_focus = sensitive;
|
|
}
|
|
|
|
_showClock() {
|
|
if (this._activePage === this._clock)
|
|
return;
|
|
|
|
this._activePage = this._clock;
|
|
|
|
this._adjustment.ease(0, {
|
|
duration: CROSSFADE_TIME,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
onComplete: () => this._maybeDestroyAuthPrompt(),
|
|
});
|
|
}
|
|
|
|
_showPrompt() {
|
|
this._ensureAuthPrompt();
|
|
|
|
if (this._activePage === this._promptBox)
|
|
return;
|
|
|
|
this._activePage = this._promptBox;
|
|
|
|
this._adjustment.ease(1, {
|
|
duration: CROSSFADE_TIME,
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
});
|
|
}
|
|
|
|
_setTransitionProgress(progress) {
|
|
this._promptBox.visible = progress > 0;
|
|
this._clock.visible = progress < 1;
|
|
|
|
const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
|
|
|
|
this._promptBox.set({
|
|
opacity: 255 * progress,
|
|
scale_x: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * progress,
|
|
scale_y: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * progress,
|
|
translation_y: FADE_OUT_TRANSLATION * (1 - progress) * scaleFactor,
|
|
});
|
|
|
|
this._clock.set({
|
|
opacity: 255 * (1 - progress),
|
|
scale_x: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * (1 - progress),
|
|
scale_y: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * (1 - progress),
|
|
translation_y: -FADE_OUT_TRANSLATION * progress * scaleFactor,
|
|
});
|
|
}
|
|
|
|
_fail() {
|
|
this._showClock();
|
|
this.emit('failed');
|
|
}
|
|
|
|
_onReset(authPrompt, beginRequest) {
|
|
let userName;
|
|
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
|
|
this._authPrompt.setUser(this._user);
|
|
userName = this._userName;
|
|
} else {
|
|
userName = null;
|
|
}
|
|
|
|
this._authPrompt.begin({ userName });
|
|
}
|
|
|
|
_escape() {
|
|
if (this.allowCancel)
|
|
this._authPrompt.cancel();
|
|
}
|
|
|
|
_swipeBegin(tracker, monitor) {
|
|
if (monitor !== Main.layoutManager.primaryIndex)
|
|
return;
|
|
|
|
this._adjustment.remove_transition('value');
|
|
|
|
this._ensureAuthPrompt();
|
|
|
|
let progress = this._adjustment.value;
|
|
tracker.confirmSwipe(this._stack.height,
|
|
[0, 1],
|
|
progress,
|
|
Math.round(progress));
|
|
}
|
|
|
|
_swipeUpdate(tracker, progress) {
|
|
this._adjustment.value = progress;
|
|
}
|
|
|
|
_swipeEnd(tracker, duration, endProgress) {
|
|
this._activePage = endProgress
|
|
? this._promptBox
|
|
: this._clock;
|
|
|
|
this._adjustment.ease(endProgress, {
|
|
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
|
duration,
|
|
onComplete: () => {
|
|
if (this._activePage === this._clock)
|
|
this._maybeDestroyAuthPrompt();
|
|
},
|
|
});
|
|
}
|
|
|
|
_otherUserClicked() {
|
|
Gdm.goto_login_session_sync(null);
|
|
|
|
this._authPrompt.cancel();
|
|
}
|
|
|
|
_onDestroy() {
|
|
this.popModal();
|
|
|
|
if (this._idleWatchId) {
|
|
this._idleMonitor.remove_watch(this._idleWatchId);
|
|
this._idleWatchId = 0;
|
|
}
|
|
|
|
if (this._monitorsChangedId) {
|
|
Main.layoutManager.disconnect(this._monitorsChangedId);
|
|
delete this._monitorsChangedId;
|
|
}
|
|
|
|
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
|
if (this._scaleChangedId) {
|
|
themeContext.disconnect(this._scaleChangedId);
|
|
delete this._scaleChangedId;
|
|
}
|
|
|
|
if (this._gdmClient) {
|
|
this._gdmClient = null;
|
|
delete this._gdmClient;
|
|
}
|
|
}
|
|
|
|
cancel() {
|
|
if (this._authPrompt)
|
|
this._authPrompt.cancel();
|
|
}
|
|
|
|
addCharacter(unichar) {
|
|
this._showPrompt();
|
|
this._authPrompt.addCharacter(unichar);
|
|
}
|
|
|
|
finish(onComplete) {
|
|
this._ensureAuthPrompt();
|
|
this._authPrompt.finish(onComplete);
|
|
}
|
|
|
|
open(timestamp) {
|
|
this.show();
|
|
|
|
if (this._isModal)
|
|
return true;
|
|
|
|
let modalParams = {
|
|
timestamp,
|
|
actionMode: Shell.ActionMode.UNLOCK_SCREEN,
|
|
};
|
|
if (!Main.pushModal(this, modalParams))
|
|
return false;
|
|
|
|
this._isModal = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
activate() {
|
|
this._showPrompt();
|
|
}
|
|
|
|
popModal(timestamp) {
|
|
if (this._isModal) {
|
|
Main.popModal(this, timestamp);
|
|
this._isModal = false;
|
|
}
|
|
}
|
|
});
|