Compare commits

...

36 Commits

Author SHA1 Message Date
Georges Basile Stavracas Neto
87a76a5757
appDisplay: Close popup when dragging
When a drag starts inside a folder, and the cursor moves to
outside it, close the currently opened folder popup.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:04 -03:00
Georges Basile Stavracas Neto
717ec0f8a4
folderView: Allow moving to specific position
As of now, the only way to add an app icon to a folder is
by dragging it to the folder icon itself. Even though we
allow opening the folder popup when hovering the icon,
dropping an app icon there wouldn't work.

Make the folder view add the app icon to it's GSettings
key (which will trigger _redisplay() and will show the
new icon) when dropping to a specific position.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:03 -03:00
Georges Basile Stavracas Neto
854922866b
appIcon: Create and delete folders with DnD
Create a new folder when dropping an icon over another
icon. Try and find a good folder name by looking into
the categories of the applications.

Delete the folder when removing the last icon.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:03 -03:00
Georges Basile Stavracas Neto
40ad9ab18c
appIcon: Show folder preview when dragging over
WIP

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:03 -03:00
Georges Basile Stavracas Neto
55eb949def
baseViewIcon: Introduce base class for view icons
Right now, only AppIcon supports being dragged. In the future,
however, both app and folder icons will be reorderable, and to
avoid copying the same code between FolderIcon and AppIcon,
add a new base class BaseViewIcon that contains the shared code
between them.

Adding this new base class also has the side effect that it
already allows for folder icons to be dragged, although full
support for that will come in next commits.

Because the Dash icons are not drop targets themselves, add a
tiny DashIcon class, which is an AppDisplay.AppIcon subclass,
and disable all DND drop code from it.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:03 -03:00
Georges Basile Stavracas Neto
0596848c27
appIcon: Use a real BaseIcon as the drag actor
Moving an app icon to other positions is semantically different
to dragging an actor to the dash; the act of moving should itself
be semantic, in that we should feel like we are moving the actual
icon.

Currently, AppIcon gives the DnD code a simplified version of itself,
with just its icon, instead of a complete copy with the label.

Make AppIcon create a new IconGrid.BaseIcon and use it as the drag
actor.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:02 -03:00
Georges Basile Stavracas Neto
0bdcf2958f
iconGrid: Apply delay to easing state
Also following design suggestion, add a small delay to the icons
moving so as to give the impression that they're moving in order.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:02 -03:00
Georges Basile Stavracas Neto
ac3bc03f3f
iconGrid: Implicitly animate icon positions
Add a proper easing state, and animate icon positions using
Clutter implicit animations.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:02 -03:00
Georges Basile Stavracas Neto
bf322cd51a
appIcon: Scale and fade itself when starting drag
As per design direction, scale and fade the app icon
when starting dragging it, and show it again if the
drop is accepted. Clutter takes care of animating the
rest of icon positions through implicit animations.

Scale and fade the dragged icon while it's being dragged.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:02 -03:00
Georges Basile Stavracas Neto
6e3696baad
allView, folderView: Implement moving icons
This makes use of the new BaseAppIcon.moveItem() API.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:01 -03:00
Georges Basile Stavracas Neto
4056c56800
allView: Add support for custom positioning
Use the new 'icon-grid-layout' key to sort the applications and folders
as it is saved. Applications that are not in the key are appended, and
when compared to each other, fallback to alphabetical order.

Because the 'icon-grid-layout' key is, by default, empty, this means that
all icons and folders are sorted alphabetically the first time they are
displayed, preserving the current behavior.

This commit does not add any way to change the order without modifying
the GSettings key directly.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:01 -03:00
Georges Basile Stavracas Neto
ebf2610140
folderView: Move DnD functions to BaseAppView
It will be much easier to handle Drag n' Drop with BaseAppView
implementing default handlers for it.

Move handleDragOver() and acceptDrop() to BaseAppView.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:01 -03:00
Georges Basile Stavracas Neto
a9e7b7853a
iconGrid: Add item nudge API
Nudging an item can be done via one side, or both. This is
essentially a set of helpers for Drag n' Drop to use. The
x and y values are relative to the icon grid itself.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:00 -03:00
Georges Basile Stavracas Neto
0dd430f2f4
appDisplay: Add moveItem()
This is a handy function to implement changing an icon's position
in the grid.

WIP: better commit message

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:00 -03:00
Georges Basile Stavracas Neto
ca01b0287e
allView: Don't sort icons
We are moving towards being able to move icons to custom
positions. To achieve that, the icon grid should stop
sort its icons.

Remove the sorting code from AllView and BaseAppView.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:00 -03:00
Georges Basile Stavracas Neto
37aa5d0faf
gschema: Add the 'icons-data' key
This is the key that will store the icons of the icon grid. It is a
sorted array of application ids, and the order of items saved in this
key corresponds to the icon order.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:33:00 -03:00
Georges Basile Stavracas Neto
2e4fb404a7
controlsManager: Don't fade icon grid while dragging
As pointed out by designers, fading it signals that the
icon grid is not a drop target, when now it actually is.

Remove the fade effect applied to the icon grid when
dragging.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:32:59 -03:00
Georges Basile Stavracas Neto
11ce7829bc
allView: Scale in when moving icons from folders
App icons inside folders are already animated when the folder is
opened, but moving an app icon from a folder doesn't, making the
transition abrupt.

Fortunately, it's easy to detect icons that were previously hidden
but are not anymore.

Add an animation to these icons when showing.

WIP: tentatively using the Tweener parameters from Endless OS.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:32:59 -03:00
Georges Basile Stavracas Neto
52257f5137
folderIcon: Add visual drag-over feedback
WIP: This is not exactly what was discussed on IRC, but
it's looking alright as a first iteration. Design feedback
welcomed.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:32:59 -03:00
Georges Basile Stavracas Neto
74077b0f6e
allView: Remove icon from folder when dropping outside
When dropping an app icon to outside the folder, remove the
app from that folder. GNOME Shell is already smart enough
to figure out the setting changes and update the icons.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:32:59 -03:00
Georges Basile Stavracas Neto
241b4cd1c8
allView: Set delegate field
DnD still relies on the _delegate field being set, so
set it.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:32:59 -03:00
Georges Basile Stavracas Neto
79f9391c00
allView: Switch pages when dragging above or below the grid
This is necessary for being able to drag application icons
to folders in different pages.

Add a drag motion handler to AllView and handle overshoots
when dragging. Only handle it when dragging from AllView.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:32:58 -03:00
Georges Basile Stavracas Neto
6da23c8d4d
appIcon: Always pass parent view
We will soon need to know which view this icon belongs to,
so add an extra parameter to the constructor to store the
parent view.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:32:58 -03:00
Georges Basile Stavracas Neto
bb9f05843f
folderIcon: Update folder icon after dropping
After dropping an application into the folder icon, the
list of applications is updated but the folder icon itself
is not.

Introduce BaseIcon.update() and call it from FolderIcon
when redisplaying.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:32:58 -03:00
Georges Basile Stavracas Neto
cc9f949b65
folderIcon: Allow dropping application icons
Connect to the overview signals related to Drag n' Drop, and
allow dropping application icons in it. Dropping an icon
appends the application id to the folder's GSettings key.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:32:58 -03:00
Georges Basile Stavracas Neto
23191ec239
appDisplay: Add event blocker inhibition API
The event blocker is an actor that is added in between the
icon grid and the app folder popup in order to guarantee
that clicking outside the app folder will collapse it.

However, the next patch will require allowing dragging events
to be passed to folder icons, and the event blocker gets in
our way here, preventing drag n' drop to work properly.

Add an API to inhibit the event blocker. This API will be
used by the app folders while an item is dragged.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/603
2019-08-02 16:32:57 -03:00
Georges Basile Stavracas Neto
038917e5f1
allView: Redisplay on folder changes
Now that redisplaying is a lightweight operation that only
adds and removes what changed, we don't need to just refilter
the app icons in AllView when a folder changes.

Call _redisplay() in AllView when folders change.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/645
2019-08-02 16:32:57 -03:00
Georges Basile Stavracas Neto
f214c5b572
baseAppView: Remove unused BaseAppView.addItem
Now that BaseAppView does not allow for subclasses to add
and remove items directly, the addItem() method can be
removed.

Remove BaseAppView.addItem().

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/645
2019-08-02 16:32:57 -03:00
Georges Basile Stavracas Neto
9da49606f7
allView, folderView: Only add icons once
FolderView and AllView currently check if the item is
present in the BaseAppView._items map, in order to avoid
adding the same icon multiple times.

Now that BaseAppView._loadGrid() has a different role
-- it returns a list with all app icons, and BaseAppView
diffs with the current list of app icons -- checking the
BaseAppView._items map is wrong.

Make sure there are no duplicated items in the temporary
array returned by all _loadGrid() implementations. Remove
the now unused BaseAppView.hasItem() method.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/645
2019-08-02 16:32:57 -03:00
Georges Basile Stavracas Neto
c13efe96dc
baseAppView: Only add and remove when necessary
BaseAppView currently removes all icons, and readds them, every
time the list of app icons needs to be redisplayed. In order to
allow animating app icon positions in the future, however, we
cannot destroy the actors of the app icons.

Previous commits paved the way for us to do differential loading,
i.e. add only the icons that were added, and remove only what was
removed.

Make the BaseAppView effectively implement differential loading.
The BaseAppView.removeAll() method is removed, since we do not
remove all icons anymore. BaseAppView._loadApps() now returns an
array with the new apps, instead of putting them directly at the
BaseAppView lists.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/645
2019-08-02 16:32:56 -03:00
Georges Basile Stavracas Neto
47d2f4dbeb
baseAppView: Add only non-added icons, in order
In the close future, BaseAppView will only add new icons (compared
to the remove all + readd all approach that is now). With that, the
items in the this._allItems array will be iterated multiple times,
but items can only be added once, and in the order they're in the
array.

Add the items in the this._allItems array passing the index they
should be added, and don't add icons with a parent already set.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/645
2019-08-02 16:32:56 -03:00
Georges Basile Stavracas Neto
48a2b6cb0b
baseAppView: Call loadGrid() directly
Now that the three views follow the exact same loading routine
(remove all + load apps + load grid), we don't need each view
call loadGrid() directly anymore.

This is an important step in order to animate adding and removing
icons, since now we can diff old and new app icons properly.

Move all calls to BaseAppView.loadGrid() to a single one after
BaseAppView._loadApps(). Also add the underscore prefix, since
this is now considered a protected function.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/645
2019-08-02 16:32:56 -03:00
Georges Basile Stavracas Neto
ce78e8ae54
frequentView: Use BaseAppView.addItem() and loadGrid()
FrequentView is another view that is slightly not unified with how
BaseAppView expects subclasses to load app icons. Instead of using
BaseAppView.addItem() and then calling BaseAppview.loadGrid(), it
adds the app icons directly to the icon grid.

Make FrequentView add icons using BaseAppview.addItem(), and load
the icons using BaseAppView.loadGrid().

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/645
2019-08-02 16:32:56 -03:00
Georges Basile Stavracas Neto
abe9b82c48
folderIcon: Move app icon loading to FolderView
Future patches will diff the old and new icons of views, in order to
animate them when necessary (e.g. adding an app icon to a folder, or
from a folder back to the app grid). In order to do that, all views
must be streamlined in how they load app icons.

Currently, FrequentView and AllView are already following the behavior
expected by BaseAppView, but FolderView isn't. Its icons are loaded by
FolderIcon, and FolderView doesn't implement BaseView._loadApps(),
which makes it impossible to diff old and new apps.

Move the app icon loading routine from FolderIcon to FolderView, by
implementing the _loadApps() method.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/645
2019-08-02 16:32:56 -03:00
Georges Basile Stavracas Neto
6c85bd6aeb
shell/app-system: Monitor for icon theme changes
Whenever an app is installed, the usual routine is
to run 'gtk-update-icon-cache' after installing all
of the app's files.

The side effect of that is that the .desktop file of
the application is installed before the icon theme
is updated. By the time GAppInfoMonitor emits the
'changed' signal, the icon theme is not yet updated,
leading to StIcon use the fallback icon.

Under some circumstances (e.g. on very slow spinning
disks) the app icon is never actually loaded, and we
see the fallback icon forever.

Monitor the icon theme for changes when an app is
installed. Try as many as 6 times before giving up
on detecting an icon theme update.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/661
2019-08-02 16:31:54 -03:00
Georges Basile Stavracas Neto
06a7ab871f
st/texture-cache: Monitor GtkIconTheme changes
The texture cache, right now, only monitors for
complete theme changes. If the contents of the
icon theme change, however, the texture cache
isn't properly invalidated.

This manifests itself as a randomly reproducible
bug when installing an app; the app icon may be
the fallback forever, or as long as something else
updates the icon theme.

Watch for the GtkIconTheme:changed signal, and
evict the texture cache when the theme changes.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/661
2019-08-02 16:31:39 -03:00
9 changed files with 1355 additions and 234 deletions

View File

@ -109,6 +109,10 @@
the shell. the shell.
</description> </description>
</key> </key>
<key name="icons-data" type="a{sv}">
<default>[]</default>
<summary>Data about the icons in the icon grid</summary>
</key>
<child name="keybindings" schema="org.gnome.shell.keybindings"/> <child name="keybindings" schema="org.gnome.shell.keybindings"/>
</schema> </schema>

View File

@ -1514,6 +1514,9 @@ StScrollBar {
border-image: none; border-image: none;
background-image: none; background-image: none;
} }
&:drop .overview-icon {
background-color: transparentize($selected_bg_color,.15);
}
&:active .overview-icon, &:active .overview-icon,
&:checked .overview-icon { &:checked .overview-icon {
background-color: transparentize(darken($osd_bg_color,10%), 0.5); background-color: transparentize(darken($osd_bg_color,10%), 0.5);

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,32 @@ function getAppFromSource(source) {
} }
} }
var DashIcon = class DashIcon extends AppDisplay.AppIcon {
constructor(app) {
super(app, null, {
setSizeManually: true,
showLabel: false
});
}
// Disable all DnD methods
_onDragBegin() {
}
_onDragEnd() {
}
handleDragOver() {
return DND.DragMotionResult.CONTINUE;
}
acceptDrop() {
return false;
}
}
// A container like StBin, but taking the child's scale into account // A container like StBin, but taking the child's scale into account
// when requesting a size // when requesting a size
var DashItemContainer = GObject.registerClass( var DashItemContainer = GObject.registerClass(
@ -475,19 +501,7 @@ var Dash = class Dash {
} }
_createAppItem(app) { _createAppItem(app) {
let appIcon = new AppDisplay.AppIcon(app, let appIcon = new DashIcon(app);
{ setSizeManually: true,
showLabel: false });
if (appIcon._draggable) {
appIcon._draggable.connect('drag-begin',
() => {
appIcon.actor.opacity = 50;
});
appIcon._draggable.connect('drag-end',
() => {
appIcon.actor.opacity = 255;
});
}
appIcon.connect('menu-state-changed', appIcon.connect('menu-state-changed',
(appIcon, opened) => { (appIcon, opened) => {

View File

@ -29,6 +29,27 @@ var AnimationDirection = {
var APPICON_ANIMATION_OUT_SCALE = 3; var APPICON_ANIMATION_OUT_SCALE = 3;
var APPICON_ANIMATION_OUT_TIME = 0.25; var APPICON_ANIMATION_OUT_TIME = 0.25;
const LEFT_DIVIDER_LEEWAY = 30;
const RIGHT_DIVIDER_LEEWAY = 30;
const NUDGE_ANIMATION_TYPE = Clutter.AnimationMode.EASE_OUT_ELASTIC;
const NUDGE_DURATION = 800;
const NUDGE_RETURN_ANIMATION_TYPE = Clutter.AnimationMode.EASE_OUT_QUINT;
const NUDGE_RETURN_DURATION = 300;
const NUDGE_FACTOR = 0.33;
const ICON_POSITION_DELAY = 25;
var DragLocation = {
DEFAULT: 0,
ON_ICON: 1,
START_EDGE: 2,
END_EDGE: 3,
EMPTY_AREA: 4,
}
var BaseIcon = GObject.registerClass( var BaseIcon = GObject.registerClass(
class BaseIcon extends St.Bin { class BaseIcon extends St.Bin {
_init(label, params) { _init(label, params) {
@ -142,6 +163,10 @@ class BaseIcon extends St.Bin {
// animating. // animating.
zoomOutActor(this.child); zoomOutActor(this.child);
} }
update() {
this._createIconTexture(this.iconSize);
}
}); });
function clamp(value, min, max) { function clamp(value, min, max) {
@ -342,6 +367,7 @@ var IconGrid = GObject.registerClass({
let y = box.y1 + this.topPadding; let y = box.y1 + this.topPadding;
let columnIndex = 0; let columnIndex = 0;
let rowIndex = 0; let rowIndex = 0;
let nChanged = 0;
for (let i = 0; i < children.length; i++) { for (let i = 0; i < children.length; i++) {
let childBox = this._calculateChildBox(children[i], x, y, box); let childBox = this._calculateChildBox(children[i], x, y, box);
@ -351,7 +377,16 @@ var IconGrid = GObject.registerClass({
} else { } else {
if (!animating) if (!animating)
children[i].opacity = 255; children[i].opacity = 255;
// Figure out how much delay to apply
if (!childBox.equal(children[i].get_allocation_box()))
nChanged++;
children[i].save_easing_state();
children[i].set_easing_mode(Clutter.AnimationMode.EASE_OUT_QUAD);
children[i].set_easing_delay(ICON_POSITION_DELAY * nChanged);
children[i].allocate(childBox, flags); children[i].allocate(childBox, flags);
children[i].restore_easing_state();
} }
columnIndex++; columnIndex++;
@ -691,6 +726,22 @@ var IconGrid = GObject.registerClass({
this.add_actor(item.actor); this.add_actor(item.actor);
} }
moveItem(item, newPosition) {
if (!this.contains(item.actor)) {
log('Cannot move item not contained by the IconGrid');
return;
}
let children = this.get_children();
let visibleChildren = children.filter(c => c.is_visible());
let visibleChildAtPosition = visibleChildren[newPosition];
let realPosition = children.indexOf(visibleChildAtPosition);
this.set_child_at_index(item.actor, realPosition);
return realPosition;
}
removeItem(item) { removeItem(item) {
this.remove_child(item.actor); this.remove_child(item.actor);
} }
@ -787,6 +838,223 @@ var IconGrid = GObject.registerClass({
} }
return GLib.SOURCE_REMOVE; return GLib.SOURCE_REMOVE;
} }
// Drag n' Drop methods
nudgeItemsAtIndex(index, dragLocation) {
// No nudging when the cursor is in an empty area
if (dragLocation == DragLocation.EMPTY_AREA || dragLocation == DragLocation.ON_ICON)
return;
let children = this.get_children().filter(c => c.is_visible());
let nudgeIndex = index;
let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
if (dragLocation != DragLocation.START_EDGE) {
let leftItem = children[nudgeIndex - 1];
let offset = rtl ? Math.floor(this._hItemSize * NUDGE_FACTOR) : Math.floor(-this._hItemSize * NUDGE_FACTOR);
this._animateNudge(leftItem, NUDGE_ANIMATION_TYPE, NUDGE_DURATION, offset);
}
// Nudge the icon to the right if we are the first item or not at the
// end of row
if (dragLocation != DragLocation.END_EDGE) {
let rightItem = children[nudgeIndex];
let offset = rtl ? Math.floor(-this._hItemSize * NUDGE_FACTOR) : Math.floor(this._hItemSize * NUDGE_FACTOR);
this._animateNudge(rightItem, NUDGE_ANIMATION_TYPE, NUDGE_DURATION, offset);
}
}
removeNudges() {
let children = this.get_children().filter(c => c.is_visible());
for (let index = 0; index < children.length; index++) {
this._animateNudge(children[index],
NUDGE_RETURN_ANIMATION_TYPE,
NUDGE_RETURN_DURATION,
0);
}
}
_animateNudge(item, animationType, duration, offset) {
if (!item)
return;
item.save_easing_state();
item.set_easing_mode(animationType);
item.set_easing_duration(duration);
item.translation_x = offset;
item.restore_easing_state();
}
// This function is overriden by the PaginatedIconGrid subclass so we can
// take into account the extra space when dragging from a folder
_calculateDndRow(y) {
let rowHeight = this._getVItemSize() + this._getSpacing();
return Math.floor(y / rowHeight);
}
// Returns the drop point index or -1 if we can't drop there
canDropAt(x, y) {
// This is an complex calculation, but in essence, we divide the grid
// as:
//
// left empty space
// | left padding right padding
// | | width without padding |
// +--------+---+---------------------------------------+-----+
// | | | | | | | |
// | | | | | | | |
// | | |--------+-----------+----------+-------| |
// | | | | | | | |
// | | | | | | | |
// | | |--------+-----------+----------+-------| |
// | | | | | | | |
// | | | | | | | |
// | | |--------+-----------+----------+-------| |
// | | | | | | | |
// | | | | | | | |
// +--------+---+---------------------------------------+-----+
//
// The left empty space is immediately discarded, and ignored in all
// calculations.
//
// The width (with paddings) is used to determine if we're dragging
// over the left or right padding, and which column is being dragged
// on.
//
// Finally, the width without padding is used to figure out where in
// the icon (start edge, end edge, on it, etc) the cursor is.
let [nColumns, usedWidth] = this._computeLayout(this.width);
let leftEmptySpace;
switch (this._xAlign) {
case St.Align.START:
leftEmptySpace = 0;
break;
case St.Align.MIDDLE:
leftEmptySpace = Math.floor((this.width - usedWidth) / 2);
break;
case St.Align.END:
leftEmptySpace = availWidth - usedWidth;
}
x -= leftEmptySpace;
y -= this.topPadding;
let row = this._calculateDndRow(y);
// Correct sx to handle the left padding to correctly calculate
// the column
let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
let gridX = x - this.leftPadding;
let widthWithoutPadding = usedWidth - this.leftPadding - this.rightPadding;
let columnWidth = widthWithoutPadding / nColumns;
let column;
if (x < this.leftPadding)
column = 0;
else if (x > usedWidth - this.rightPadding)
column = nColumns - 1;
else
column = Math.floor(gridX / columnWidth);
let isFirstIcon = column == 0;
let isLastIcon = column == nColumns - 1;
// If we're outside of the grid, we are in an invalid drop location
if (x < 0 || x > usedWidth)
return [-1, DragLocation.DEFAULT];
let children = this.get_children().filter(c => c.is_visible());
let childIndex = Math.min((row * nColumns) + column, children.length);
// If we're above the grid vertically, we are in an invalid
// drop location
if (childIndex < 0)
return [-1, DragLocation.DEFAULT];
// If we're past the last visible element in the grid,
// we might be allowed to drop there.
if (childIndex >= children.length)
return [children.length, DragLocation.EMPTY_AREA];
let child = children[childIndex];
let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight] = child.get_preferred_size();
// This is the width of the cell that contains the icon
// (excluding spacing between cells)
let childIconWidth = Math.max(this._getHItemSize(), childNaturalWidth);
// Calculate the original position of the child icon (prior to nudging)
let childX;
if (rtl)
childX = widthWithoutPadding - (column * columnWidth) - childIconWidth;
else
childX = column * columnWidth;
let iconLeftX = childX + LEFT_DIVIDER_LEEWAY;
let iconRightX = childX + childIconWidth - RIGHT_DIVIDER_LEEWAY
let dropIndex;
let dragLocation;
x -= this.leftPadding;
if (x < iconLeftX) {
// We are to the left of the icon target
if (isFirstIcon || x < 0) {
// We are before the leftmost icon on the grid
if (rtl) {
dropIndex = childIndex + 1;
dragLocation = DragLocation.END_EDGE;
} else {
dropIndex = childIndex;
dragLocation = DragLocation.START_EDGE;
}
} else {
// We are between the previous icon (next in RTL) and this one
if (rtl)
dropIndex = childIndex + 1;
else
dropIndex = childIndex;
dragLocation = DragLocation.DEFAULT;
}
} else if (x >= iconRightX) {
// We are to the right of the icon target
if (childIndex >= children.length) {
// We are beyond the last valid icon
// (to the right of the app store / trash can, if present)
dropIndex = -1;
dragLocation = DragLocation.DEFAULT;
} else if (isLastIcon || x >= widthWithoutPadding) {
// We are beyond the rightmost icon on the grid
if (rtl) {
dropIndex = childIndex;
dragLocation = DragLocation.START_EDGE;
} else {
dropIndex = childIndex + 1;
dragLocation = DragLocation.END_EDGE;
}
} else {
// We are between this icon and the next one (previous in RTL)
if (rtl)
dropIndex = childIndex;
else
dropIndex = childIndex + 1;
dragLocation = DragLocation.DEFAULT;
}
} else {
// We are over the icon target area
dropIndex = childIndex;
dragLocation = DragLocation.ON_ICON;
}
return [dropIndex, dragLocation];
}
}); });
var PaginatedIconGrid = GObject.registerClass({ var PaginatedIconGrid = GObject.registerClass({
@ -839,10 +1107,21 @@ var PaginatedIconGrid = GObject.registerClass({
let x = box.x1 + leftEmptySpace + this.leftPadding; let x = box.x1 + leftEmptySpace + this.leftPadding;
let y = box.y1 + this.topPadding; let y = box.y1 + this.topPadding;
let columnIndex = 0; let columnIndex = 0;
let nChanged = 0;
for (let i = 0; i < children.length; i++) { for (let i = 0; i < children.length; i++) {
let childBox = this._calculateChildBox(children[i], x, y, box); let childBox = this._calculateChildBox(children[i], x, y, box);
// Figure out how much delay to apply
if (!childBox.equal(children[i].get_allocation_box()))
nChanged++;
children[i].save_easing_state();
children[i].set_easing_mode(Clutter.AnimationMode.EASE_OUT_QUAD);
children[i].set_easing_delay(ICON_POSITION_DELAY * nChanged);
children[i].allocate(childBox, flags); children[i].allocate(childBox, flags);
children[i].restore_easing_state();
children[i].show(); children[i].show();
columnIndex++; columnIndex++;
@ -861,6 +1140,23 @@ var PaginatedIconGrid = GObject.registerClass({
} }
// Overridden from IconGrid // Overridden from IconGrid
_calculateDndRow(y) {
let row = super._calculateDndRow(y);
// If there's no extra space, just return the current value and maintain
// the same behavior when without a folder opened.
if (!this._extraSpaceData)
return row;
let [ baseRow, nRowsUp, nRowsDown ] = this._extraSpaceData;
let newRow = row + nRowsUp;
if (row > baseRow)
newRow -= nRowsDown;
return newRow;
}
_getChildrenToAnimate() { _getChildrenToAnimate() {
let children = this._getVisibleChildren(); let children = this._getVisibleChildren();
let firstIndex = this._childrenPerPage * this.currentPage; let firstIndex = this._childrenPerPage * this.currentPage;

View File

@ -424,17 +424,6 @@ var ControlsManager = class {
layout.connect('allocation-changed', this._updateWorkspacesGeometry.bind(this)); layout.connect('allocation-changed', this._updateWorkspacesGeometry.bind(this));
Main.overview.connect('showing', this._updateSpacerVisibility.bind(this)); Main.overview.connect('showing', this._updateSpacerVisibility.bind(this));
Main.overview.connect('item-drag-begin', () => {
let activePage = this.viewSelector.getActivePage();
if (activePage != ViewSelector.ViewPage.WINDOWS)
this.viewSelector.fadeHalf();
});
Main.overview.connect('item-drag-end', () => {
this.viewSelector.fadeIn();
});
Main.overview.connect('item-drag-cancelled', () => {
this.viewSelector.fadeIn();
});
} }
_updateWorkspacesGeometry() { _updateWorkspacesGeometry() {

View File

@ -14,6 +14,14 @@
#include "shell-app-system-private.h" #include "shell-app-system-private.h"
#include "shell-global.h" #include "shell-global.h"
#include "shell-util.h" #include "shell-util.h"
#include "st.h"
/* Rescan for at most RESCAN_TIMEOUT_MS * MAX_RESCAN_RETRIES. That
* should be plenty of time for even a slow spinning drive to update
* the icon cache.
*/
#define RESCAN_TIMEOUT_MS 2500
#define MAX_RESCAN_RETRIES 6
/* Vendor prefixes are something that can be preprended to a .desktop /* Vendor prefixes are something that can be preprended to a .desktop
* file name. Undo this. * file name. Undo this.
@ -51,6 +59,9 @@ struct _ShellAppSystemPrivate {
GHashTable *id_to_app; GHashTable *id_to_app;
GHashTable *startup_wm_class_to_id; GHashTable *startup_wm_class_to_id;
GList *installed_apps; GList *installed_apps;
guint rescan_icons_timeout_id;
guint n_rescan_retries;
}; };
static void shell_app_system_finalize (GObject *object); static void shell_app_system_finalize (GObject *object);
@ -157,12 +168,54 @@ stale_app_remove_func (gpointer key,
return app_is_stale (value); return app_is_stale (value);
} }
static gboolean
rescan_icon_theme_cb (gpointer user_data)
{
ShellAppSystemPrivate *priv;
ShellAppSystem *self;
StTextureCache *texture_cache;
gboolean rescanned;
self = (ShellAppSystem *) user_data;
priv = self->priv;
texture_cache = st_texture_cache_get_default ();
rescanned = st_texture_cache_rescan_icon_theme (texture_cache);
priv->n_rescan_retries++;
if (rescanned || priv->n_rescan_retries >= MAX_RESCAN_RETRIES)
{
priv->n_rescan_retries = 0;
priv->rescan_icons_timeout_id = 0;
return G_SOURCE_REMOVE;
}
return G_SOURCE_CONTINUE;
}
static void
rescan_icon_theme (ShellAppSystem *self)
{
ShellAppSystemPrivate *priv = self->priv;
priv->n_rescan_retries = 0;
if (priv->rescan_icons_timeout_id > 0)
return;
priv->rescan_icons_timeout_id = g_timeout_add (RESCAN_TIMEOUT_MS,
rescan_icon_theme_cb,
self);
}
static void static void
installed_changed (GAppInfoMonitor *monitor, installed_changed (GAppInfoMonitor *monitor,
gpointer user_data) gpointer user_data)
{ {
ShellAppSystem *self = user_data; ShellAppSystem *self = user_data;
rescan_icon_theme (self);
scan_startup_wm_class_to_id (self); scan_startup_wm_class_to_id (self);
g_hash_table_foreach_remove (self->priv->id_to_app, stale_app_remove_func, NULL); g_hash_table_foreach_remove (self->priv->id_to_app, stale_app_remove_func, NULL);
@ -200,6 +253,7 @@ shell_app_system_finalize (GObject *object)
g_hash_table_destroy (priv->id_to_app); g_hash_table_destroy (priv->id_to_app);
g_hash_table_destroy (priv->startup_wm_class_to_id); g_hash_table_destroy (priv->startup_wm_class_to_id);
g_list_free_full (priv->installed_apps, g_object_unref); g_list_free_full (priv->installed_apps, g_object_unref);
g_clear_handle_id (&priv->rescan_icons_timeout_id, g_source_remove);
G_OBJECT_CLASS (shell_app_system_parent_class)->finalize (object); G_OBJECT_CLASS (shell_app_system_parent_class)->finalize (object);
} }

View File

@ -150,6 +150,14 @@ on_icon_theme_changed (StSettings *settings,
g_signal_emit (cache, signals[ICON_THEME_CHANGED], 0); g_signal_emit (cache, signals[ICON_THEME_CHANGED], 0);
} }
static void
on_gtk_icon_theme_changed (GtkIconTheme *icon_theme,
StTextureCache *self)
{
st_texture_cache_evict_icons (self);
g_signal_emit (self, signals[ICON_THEME_CHANGED], 0);
}
static void static void
st_texture_cache_init (StTextureCache *self) st_texture_cache_init (StTextureCache *self)
{ {
@ -160,6 +168,8 @@ st_texture_cache_init (StTextureCache *self)
self->priv->icon_theme = gtk_icon_theme_new (); self->priv->icon_theme = gtk_icon_theme_new ();
gtk_icon_theme_add_resource_path (self->priv->icon_theme, gtk_icon_theme_add_resource_path (self->priv->icon_theme,
"/org/gnome/shell/theme/icons"); "/org/gnome/shell/theme/icons");
g_signal_connect (self->priv->icon_theme, "changed",
G_CALLBACK (on_gtk_icon_theme_changed), self);
settings = st_settings_get (); settings = st_settings_get ();
g_signal_connect (settings, "notify::gtk-icon-theme", g_signal_connect (settings, "notify::gtk-icon-theme",
@ -1557,3 +1567,11 @@ st_texture_cache_get_default (void)
instance = g_object_new (ST_TYPE_TEXTURE_CACHE, NULL); instance = g_object_new (ST_TYPE_TEXTURE_CACHE, NULL);
return instance; return instance;
} }
gboolean
st_texture_cache_rescan_icon_theme (StTextureCache *cache)
{
StTextureCachePrivate *priv = cache->priv;
return gtk_icon_theme_rescan_if_needed (priv->icon_theme);
}

View File

@ -113,4 +113,6 @@ CoglTexture * st_texture_cache_load (StTextureCache *cache,
void *data, void *data,
GError **error); GError **error);
gboolean st_texture_cache_rescan_icon_theme (StTextureCache *cache);
#endif /* __ST_TEXTURE_CACHE_H__ */ #endif /* __ST_TEXTURE_CACHE_H__ */