appDisplay: Transform folder into a dialog
Make the AppFolderPopup behave much more like a dialog than a popup itself. To do that, remove the BoxPointer and replace it by a StBoxLayout. The dialog is is also bind-constrained to the view selector. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/896
This commit is contained in:
parent
e573d739af
commit
f46d10c4f9
@ -86,14 +86,16 @@ $app_grid_fg_color: #fff;
|
|||||||
}
|
}
|
||||||
|
|
||||||
// expanded folder
|
// expanded folder
|
||||||
.app-folder-popup {
|
.app-folder-dialog {
|
||||||
-arrow-border-radius: 8px;
|
border-radius: 8px;
|
||||||
-arrow-background-color: transparentize(darken($osd_bg_color,10%), 0.5);
|
spacing: 24px;
|
||||||
-arrow-base: 24px;
|
background-color: transparentize(darken($osd_bg_color,10%), 0.05);
|
||||||
-arrow-rise: 11px;
|
}
|
||||||
|
.app-folder-dialog-container {
|
||||||
|
padding: 12px;
|
||||||
|
width: 800px;
|
||||||
|
height: 600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-folder-popup-bin { padding: $base_padding - 1px; }
|
|
||||||
|
|
||||||
.app-folder-icon {
|
.app-folder-icon {
|
||||||
padding: $base_padding;
|
padding: $base_padding;
|
||||||
|
@ -5,7 +5,6 @@ const { Clutter, Gio, GLib, GObject, Graphene, Meta, Shell, St } = imports.gi;
|
|||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
|
||||||
const AppFavorites = imports.ui.appFavorites;
|
const AppFavorites = imports.ui.appFavorites;
|
||||||
const BoxPointer = imports.ui.boxpointer;
|
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
const GrabHelper = imports.ui.grabHelper;
|
const GrabHelper = imports.ui.grabHelper;
|
||||||
const IconGrid = imports.ui.iconGrid;
|
const IconGrid = imports.ui.iconGrid;
|
||||||
@ -41,6 +40,8 @@ var PAGE_SWITCH_TIME = 250;
|
|||||||
var APP_ICON_SCALE_IN_TIME = 500;
|
var APP_ICON_SCALE_IN_TIME = 500;
|
||||||
var APP_ICON_SCALE_IN_DELAY = 700;
|
var APP_ICON_SCALE_IN_DELAY = 700;
|
||||||
|
|
||||||
|
const FOLDER_DIALOG_ANIMATION_TIME = 200;
|
||||||
|
|
||||||
const OVERSHOOT_THRESHOLD = 20;
|
const OVERSHOOT_THRESHOLD = 20;
|
||||||
const OVERSHOOT_TIMEOUT = 1000;
|
const OVERSHOOT_TIMEOUT = 1000;
|
||||||
|
|
||||||
@ -1404,10 +1405,10 @@ class FolderView extends BaseAppView {
|
|||||||
this._scrollView.update_fade_effect(fadeOffset, 0);
|
this._scrollView.update_fade_effect(fadeOffset, 0);
|
||||||
|
|
||||||
// Set extra padding to avoid popup or close button being cut off
|
// Set extra padding to avoid popup or close button being cut off
|
||||||
this._grid.topPadding = Math.max(this._grid.topPadding - this._offsetForEachSide, 0);
|
this._grid.topPadding = Math.max(this._grid.topPadding, 0);
|
||||||
this._grid.bottomPadding = Math.max(this._grid.bottomPadding - this._offsetForEachSide, 0);
|
this._grid.bottomPadding = Math.max(this._grid.bottomPadding, 0);
|
||||||
this._grid.leftPadding = Math.max(this._grid.leftPadding - this._offsetForEachSide, 0);
|
this._grid.leftPadding = Math.max(this._grid.leftPadding, 0);
|
||||||
this._grid.rightPadding = Math.max(this._grid.rightPadding - this._offsetForEachSide, 0);
|
this._grid.rightPadding = Math.max(this._grid.rightPadding, 0);
|
||||||
|
|
||||||
this.set_width(this.usedWidth());
|
this.set_width(this.usedWidth());
|
||||||
this.set_height(this.usedHeight());
|
this.set_height(this.usedHeight());
|
||||||
@ -1422,7 +1423,7 @@ class FolderView extends BaseAppView {
|
|||||||
let contentBox = this.get_theme_node().get_content_box(pageBox);
|
let contentBox = this.get_theme_node().get_content_box(pageBox);
|
||||||
// We only can show icons inside the collection view boxPointer
|
// We only can show icons inside the collection view boxPointer
|
||||||
// so we have to subtract the required padding etc of the boxpointer
|
// so we have to subtract the required padding etc of the boxpointer
|
||||||
return [(contentBox.x2 - contentBox.x1) - 2 * this._offsetForEachSide, (contentBox.y2 - contentBox.y1) - 2 * this._offsetForEachSide];
|
return [contentBox.x2 - contentBox.x1, contentBox.y2 - contentBox.y1];
|
||||||
}
|
}
|
||||||
|
|
||||||
usedWidth() {
|
usedWidth() {
|
||||||
@ -1440,10 +1441,6 @@ class FolderView extends BaseAppView {
|
|||||||
return Math.min(this._grid.nRows(availWidthPerPage), maxRows);
|
return Math.min(this._grid.nRows(availWidthPerPage), maxRows);
|
||||||
}
|
}
|
||||||
|
|
||||||
setPaddingOffsets(offset) {
|
|
||||||
this._offsetForEachSide = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
_loadApps() {
|
_loadApps() {
|
||||||
let apps = [];
|
let apps = [];
|
||||||
let excludedApps = this._folder.get_strv('excluded-apps');
|
let excludedApps = this._folder.get_strv('excluded-apps');
|
||||||
@ -1599,7 +1596,7 @@ var FolderIcon = GObject.registerClass({
|
|||||||
this._removeMenuTimeout();
|
this._removeMenuTimeout();
|
||||||
this._ensurePopup();
|
this._ensurePopup();
|
||||||
this.view._scrollView.vscroll.adjustment.value = 0;
|
this.view._scrollView.vscroll.adjustment.value = 0;
|
||||||
this._openSpaceForPopup();
|
this._popup.popup();
|
||||||
}
|
}
|
||||||
|
|
||||||
getAppIds() {
|
getAppIds() {
|
||||||
@ -1694,65 +1691,21 @@ var FolderIcon = GObject.registerClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
_popupHeight() {
|
_popupHeight() {
|
||||||
let usedHeight = this.view.usedHeight() + this._popup.getOffset(St.Side.TOP) + this._popup.getOffset(St.Side.BOTTOM);
|
let usedHeight = this.view.usedHeight();
|
||||||
return usedHeight;
|
return usedHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
_openSpaceForPopup() {
|
|
||||||
this._spaceReadySignalId = this._parentView.connect('space-ready', () => {
|
|
||||||
this._parentView.disconnect(this._spaceReadySignalId);
|
|
||||||
this._spaceReadySignalId = 0;
|
|
||||||
this._popup.popup();
|
|
||||||
this._updatePopupPosition();
|
|
||||||
});
|
|
||||||
this._parentView.openSpaceForPopup(this, this._boxPointerArrowside, this.view.nRowsDisplayedAtOnce());
|
|
||||||
}
|
|
||||||
|
|
||||||
_calculateBoxPointerArrowSide() {
|
|
||||||
let spaceTop = this.y - this._parentView.getCurrentPageY();
|
|
||||||
let spaceBottom = this._parentView.height - (spaceTop + this.height);
|
|
||||||
|
|
||||||
return spaceTop > spaceBottom ? St.Side.BOTTOM : St.Side.TOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
_updatePopupSize() {
|
|
||||||
// StWidget delays style calculation until needed, make sure we use the correct values
|
|
||||||
this.view._grid.ensure_style();
|
|
||||||
|
|
||||||
let offsetForEachSide = Math.ceil((this._popup.getOffset(St.Side.TOP) +
|
|
||||||
this._popup.getOffset(St.Side.BOTTOM) -
|
|
||||||
this._popup.getCloseButtonOverlap()) / 2);
|
|
||||||
// Add extra padding to prevent boxpointer decorations and close button being cut off
|
|
||||||
this.view.setPaddingOffsets(offsetForEachSide);
|
|
||||||
this.view.adaptToSize(this._parentAvailableWidth, this._parentAvailableHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
_updatePopupPosition() {
|
|
||||||
if (!this._popup)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this._boxPointerArrowside == St.Side.BOTTOM)
|
|
||||||
this._popup.y = this.allocation.y1 + this.translation_y - this._popupHeight();
|
|
||||||
else
|
|
||||||
this._popup.y = this.allocation.y1 + this.translation_y + this.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ensurePopup() {
|
_ensurePopup() {
|
||||||
if (this._popup && !this._popupInvalidated)
|
if (this._popup && !this._popupInvalidated)
|
||||||
return;
|
return;
|
||||||
this._boxPointerArrowside = this._calculateBoxPointerArrowSide();
|
|
||||||
if (!this._popup) {
|
if (!this._popup) {
|
||||||
this._popup = new AppFolderPopup(this, this._boxPointerArrowside);
|
this._popup = new AppFolderPopup(this);
|
||||||
this._parentView.addFolderPopup(this._popup);
|
this._parentView.addFolderPopup(this._popup);
|
||||||
this._popup.connect('open-state-changed', (popup, isOpen) => {
|
this._popup.connect('open-state-changed', (popup, isOpen) => {
|
||||||
if (!isOpen)
|
if (!isOpen)
|
||||||
this.checked = false;
|
this.checked = false;
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
this._popup.updateArrowSide(this._boxPointerArrowside);
|
|
||||||
}
|
}
|
||||||
this._updatePopupSize();
|
|
||||||
this._updatePopupPosition();
|
|
||||||
this._popupInvalidated = false;
|
this._popupInvalidated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1943,43 +1896,36 @@ var AppFolderPopup = GObject.registerClass({
|
|||||||
'open-state-changed': { param_types: [GObject.TYPE_BOOLEAN] },
|
'open-state-changed': { param_types: [GObject.TYPE_BOOLEAN] },
|
||||||
},
|
},
|
||||||
}, class AppFolderPopup extends St.Widget {
|
}, class AppFolderPopup extends St.Widget {
|
||||||
_init(source, side) {
|
_init(source) {
|
||||||
super._init({
|
super._init({
|
||||||
layout_manager: new Clutter.BinLayout(),
|
layout_manager: new Clutter.BinLayout(),
|
||||||
|
style_class: 'app-folder-dialog-container',
|
||||||
visible: false,
|
visible: false,
|
||||||
// We don't want to expand really, but look
|
|
||||||
// at the layout manager of our parent...
|
|
||||||
//
|
|
||||||
// DOUBLE HACK: if you set one, you automatically
|
|
||||||
// get the effect for the other direction too, so
|
|
||||||
// we need to set the y_align
|
|
||||||
x_expand: true,
|
x_expand: true,
|
||||||
y_expand: true,
|
y_expand: true,
|
||||||
x_align: Clutter.ActorAlign.CENTER,
|
x_align: Clutter.ActorAlign.CENTER,
|
||||||
y_align: Clutter.ActorAlign.START,
|
y_align: Clutter.ActorAlign.CENTER,
|
||||||
});
|
});
|
||||||
|
this.add_constraint(new Clutter.BindConstraint({
|
||||||
|
source: Main.overview.viewSelector,
|
||||||
|
coordinate: Clutter.BindCoordinate.ALL,
|
||||||
|
}));
|
||||||
|
|
||||||
this._source = source;
|
this._source = source;
|
||||||
this._view = source.view;
|
this._view = source.view;
|
||||||
this._arrowSide = side;
|
|
||||||
|
|
||||||
this._isOpen = false;
|
this._isOpen = false;
|
||||||
this.parentOffset = 0;
|
this.parentOffset = 0;
|
||||||
|
|
||||||
this._boxPointer = new BoxPointer.BoxPointer(this._arrowSide, {
|
this._viewBox = new St.BoxLayout({
|
||||||
style_class: 'app-folder-popup-bin',
|
style_class: 'app-folder-dialog',
|
||||||
x_expand: true,
|
x_expand: true,
|
||||||
|
y_expand: true,
|
||||||
|
x_align: Clutter.ActorAlign.FILL,
|
||||||
|
y_align: Clutter.ActorAlign.FILL,
|
||||||
});
|
});
|
||||||
|
this.add_child(this._viewBox);
|
||||||
this._boxPointer.style_class = 'app-folder-popup';
|
this._viewBox.add_child(this._view);
|
||||||
this.add_actor(this._boxPointer);
|
|
||||||
this._boxPointer.bin.set_child(this._view);
|
|
||||||
|
|
||||||
this.closeButton = Util.makeCloseButton(this._boxPointer);
|
|
||||||
this.closeButton.connect('clicked', this.popdown.bind(this));
|
|
||||||
this.add_actor(this.closeButton);
|
|
||||||
|
|
||||||
this._boxPointer.bind_property('opacity', this.closeButton, 'opacity',
|
|
||||||
GObject.BindingFlags.SYNC_CREATE);
|
|
||||||
|
|
||||||
global.focus_manager.add_group(this);
|
global.focus_manager.add_group(this);
|
||||||
|
|
||||||
@ -1988,6 +1934,78 @@ var AppFolderPopup = GObject.registerClass({
|
|||||||
});
|
});
|
||||||
this._grabHelper.addActor(Main.layoutManager.overviewGroup);
|
this._grabHelper.addActor(Main.layoutManager.overviewGroup);
|
||||||
this.connect('destroy', this._onDestroy.bind(this));
|
this.connect('destroy', this._onDestroy.bind(this));
|
||||||
|
|
||||||
|
this._sourceMappedId = 0;
|
||||||
|
this._needsZoomAndFade = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_zoomAndFadeIn() {
|
||||||
|
let [sourceX, sourceY] =
|
||||||
|
this._source.get_transformed_position();
|
||||||
|
let [dialogX, dialogY] =
|
||||||
|
this.get_transformed_position();
|
||||||
|
|
||||||
|
this.set({
|
||||||
|
translation_x: sourceX - dialogX,
|
||||||
|
translation_y: sourceY - dialogY,
|
||||||
|
scale_x: this._source.width / this.width,
|
||||||
|
scale_y: this._source.height / this.height,
|
||||||
|
opacity: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.ease({
|
||||||
|
translation_x: 0,
|
||||||
|
translation_y: 0,
|
||||||
|
scale_x: 1,
|
||||||
|
scale_y: 1,
|
||||||
|
opacity: 255,
|
||||||
|
duration: FOLDER_DIALOG_ANIMATION_TIME,
|
||||||
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._needsZoomAndFade = false;
|
||||||
|
|
||||||
|
if (this._sourceMappedId === 0) {
|
||||||
|
this._sourceMappedId = this._source.connect(
|
||||||
|
'notify::mapped', this._zoomAndFadeOut.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_zoomAndFadeOut() {
|
||||||
|
if (!this._isOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this._source.mapped) {
|
||||||
|
this.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let [sourceX, sourceY] =
|
||||||
|
this._source.get_transformed_position();
|
||||||
|
let [dialogX, dialogY] =
|
||||||
|
this.get_transformed_position();
|
||||||
|
|
||||||
|
this.ease({
|
||||||
|
translation_x: sourceX - dialogX,
|
||||||
|
translation_y: sourceY - dialogY,
|
||||||
|
scale_x: this._source.width / this.width,
|
||||||
|
scale_y: this._source.height / this.height,
|
||||||
|
opacity: 0,
|
||||||
|
duration: FOLDER_DIALOG_ANIMATION_TIME,
|
||||||
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||||
|
onComplete: () => {
|
||||||
|
this.set({
|
||||||
|
translation_x: 0,
|
||||||
|
translation_y: 0,
|
||||||
|
scale_x: 1,
|
||||||
|
scale_y: 1,
|
||||||
|
opacity: 255,
|
||||||
|
});
|
||||||
|
this.hide();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this._needsZoomAndFade = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDestroy() {
|
_onDestroy() {
|
||||||
@ -1996,6 +2014,22 @@ var AppFolderPopup = GObject.registerClass({
|
|||||||
this._grabHelper.ungrab({ actor: this });
|
this._grabHelper.ungrab({ actor: this });
|
||||||
this._grabHelper = null;
|
this._grabHelper = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._sourceMappedId) {
|
||||||
|
this._source.disconnect(this._sourceMappedId);
|
||||||
|
this._sourceMappedId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vfunc_allocate(box, flags) {
|
||||||
|
let contentBox = this.get_theme_node().get_content_box(box);
|
||||||
|
this._view.adaptToSize(contentBox.get_width(), contentBox.get_height());
|
||||||
|
|
||||||
|
super.vfunc_allocate(box, flags);
|
||||||
|
|
||||||
|
// We can only start zooming after receiving an allocation
|
||||||
|
if (this._needsZoomAndFade)
|
||||||
|
this._zoomAndFadeIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
vfunc_key_press_event(keyEvent) {
|
vfunc_key_press_event(keyEvent) {
|
||||||
@ -2061,20 +2095,9 @@ var AppFolderPopup = GObject.registerClass({
|
|||||||
if (!this._isOpen)
|
if (!this._isOpen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
this._needsZoomAndFade = true;
|
||||||
this.show();
|
this.show();
|
||||||
|
|
||||||
this._boxPointer.setArrowActor(this._source);
|
|
||||||
// We need to hide the icons of the view until the boxpointer animation
|
|
||||||
// is completed so we can animate the icons after as we like without
|
|
||||||
// showing them while boxpointer is animating.
|
|
||||||
this._view.opacity = 0;
|
|
||||||
this._boxPointer.open(BoxPointer.PopupAnimation.FADE |
|
|
||||||
BoxPointer.PopupAnimation.SLIDE,
|
|
||||||
() => {
|
|
||||||
this._view.opacity = 255;
|
|
||||||
this._view.animate(IconGrid.AnimationDirection.IN);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.emit('open-state-changed', true);
|
this.emit('open-state-changed', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2082,29 +2105,12 @@ var AppFolderPopup = GObject.registerClass({
|
|||||||
if (!this._isOpen)
|
if (!this._isOpen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._grabHelper.ungrab({ actor: this });
|
this._zoomAndFadeOut();
|
||||||
|
|
||||||
this._boxPointer.close(BoxPointer.PopupAnimation.FADE |
|
this._grabHelper.ungrab({ actor: this });
|
||||||
BoxPointer.PopupAnimation.SLIDE);
|
|
||||||
this._isOpen = false;
|
this._isOpen = false;
|
||||||
this.emit('open-state-changed', false);
|
this.emit('open-state-changed', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCloseButtonOverlap() {
|
|
||||||
return this.closeButton.get_theme_node().get_length('-shell-close-overlap-y');
|
|
||||||
}
|
|
||||||
|
|
||||||
getOffset(side) {
|
|
||||||
let offset = this._boxPointer.getPadding(side);
|
|
||||||
if (this._arrowSide == side)
|
|
||||||
offset += this._boxPointer.getArrowHeight();
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateArrowSide(side) {
|
|
||||||
this._arrowSide = side;
|
|
||||||
this._boxPointer.updateArrowSide(side);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var AppIcon = GObject.registerClass({
|
var AppIcon = GObject.registerClass({
|
||||||
|
Loading…
Reference in New Issue
Block a user