Merge branch 'appicon-raising' of https://github.com/abakkk/dash-to-panel into abakkk-appicon-raising

This commit is contained in:
Jason DeRose
2021-05-02 12:54:39 -04:00
6 changed files with 894 additions and 9 deletions

View File

@@ -2,11 +2,315 @@
<!-- Generated with glade 3.36.0 -->
<interface>
<requires lib="gtk+" version="3.12"/>
<object class="GtkBox" id="animate_appicon_hover_options">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkListBox" id="animate_appicon_hover_options_listbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<child>
<object class="GtkListBoxRow" id="animate_appicon_hover_options_type_listboxrow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="activatable">False</property>
<child>
<object class="GtkBox" id="animate_appicon_hover_options_type_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_right">12</property>
<property name="margin_top">12</property>
<property name="margin_bottom">12</property>
<property name="spacing">32</property>
<child>
<object class="GtkLabel" id="animate_appicon_hover_options_type_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Animation type</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="animate_appicon_hover_options_type_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">center</property>
<items>
<item id="SIMPLE" translatable="yes">Simple</item>
<item id="RIPPLE" translatable="yes">Ripple</item>
<item id="PLANK" translatable="yes">Plank</item>
</items>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkListBoxRow" id="animate_appicon_hover_options_params_listboxrow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="activatable">False</property>
<child>
<object class="GtkGrid" id="animate_appicon_hover_options_params_grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_right">12</property>
<property name="margin_top">12</property>
<property name="margin_bottom">12</property>
<property name="row_spacing">24</property>
<property name="column_spacing">32</property>
<child>
<object class="GtkLabel" id="animate_appicon_hover_options_duration_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_right">12</property>
<property name="label" translatable="yes">Duration</property>
<property name="use_markup">True</property>
<property name="xalign">0</property>
<property name="valign">end</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkScale" id="animate_appicon_hover_options_duration_scale">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="valign">end</property>
<property name="hexpand">True</property>
<property name="adjustment">animate_appicon_hover_options_duration_adjustment</property>
<property name="round_digits">0</property>
<property name="digits">0</property>
<property name="value_pos">right</property>
<signal name="format-value" handler="animate_appicon_hover_options_duration_scale_format_value_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="animate_appicon_hover_options_extent_rotation">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Rotation</property>
<property name="xalign">0</property>
<property name="valign">end</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkScale" id="animate_appicon_hover_options_rotation_scale">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="valign">end</property>
<property name="hexpand">True</property>
<property name="adjustment">animate_appicon_hover_options_rotation_adjustment</property>
<property name="round_digits">0</property>
<property name="digits">0</property>
<property name="value_pos">right</property>
<signal name="format-value" handler="animate_appicon_hover_options_rotation_scale_format_value_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="animate_appicon_hover_options_travel_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Travel</property>
<property name="xalign">0</property>
<property name="valign">end</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkScale" id="animate_appicon_hover_options_travel_scale">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="valign">end</property>
<property name="hexpand">True</property>
<property name="adjustment">animate_appicon_hover_options_travel_adjustment</property>
<property name="round_digits">0</property>
<property name="digits">0</property>
<property name="value_pos">right</property>
<signal name="format-value" handler="animate_appicon_hover_options_travel_scale_format_value_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="animate_appicon_hover_options_zoom_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Zoom</property>
<property name="xalign">0</property>
<property name="valign">end</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkScale" id="animate_appicon_hover_options_zoom_scale">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="valign">end</property>
<property name="hexpand">True</property>
<property name="adjustment">animate_appicon_hover_options_zoom_adjustment</property>
<property name="round_digits">0</property>
<property name="digits">0</property>
<property name="value_pos">right</property>
<signal name="format-value" handler="animate_appicon_hover_options_zoom_scale_format_value_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="animate_appicon_hover_options_convexity_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Convexity</property>
<property name="xalign">0</property>
<property name="valign">end</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkScale" id="animate_appicon_hover_options_convexity_scale">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="valign">end</property>
<property name="hexpand">True</property>
<property name="adjustment">animate_appicon_hover_options_convexity_adjustment</property>
<property name="round_digits">1</property>
<property name="digits">1</property>
<property name="value_pos">right</property>
<signal name="format-value" handler="animate_appicon_hover_options_convexity_scale_format_value_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="animate_appicon_hover_options_extent_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Extent</property>
<property name="xalign">0</property>
<property name="valign">end</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkScale" id="animate_appicon_hover_options_extent_scale">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="valign">end</property>
<property name="hexpand">True</property>
<property name="adjustment">animate_appicon_hover_options_extent_adjustment</property>
<property name="round_digits">0</property>
<property name="digits">0</property>
<property name="value_pos">right</property>
<signal name="format-value" handler="animate_appicon_hover_options_extent_scale_format_value_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<object class="GtkAdjustment" id="panel_length_adjustment">
<property name="upper">100</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="animate_appicon_hover_options_duration_adjustment">
<property name="lower">0</property>
<property name="upper">300</property>
<property name="step_increment">1</property>
<property name="page_increment">5</property>
</object>
<object class="GtkAdjustment" id="animate_appicon_hover_options_rotation_adjustment">
<property name="lower">-30</property>
<property name="upper">30</property>
<property name="step_increment">1</property>
<property name="page_increment">5</property>
</object>
<object class="GtkAdjustment" id="animate_appicon_hover_options_travel_adjustment">
<property name="lower">0</property>
<property name="upper">100</property>
<property name="step_increment">1</property>
<property name="page_increment">5</property>
</object>
<object class="GtkAdjustment" id="animate_appicon_hover_options_zoom_adjustment">
<property name="lower">100</property>
<property name="upper">250</property>
<property name="step_increment">1</property>
<property name="page_increment">5</property>
</object>
<object class="GtkAdjustment" id="animate_appicon_hover_options_convexity_adjustment">
<property name="lower">0</property>
<property name="upper">3</property>
<property name="step_increment">0.1</property>
<property name="page_increment">1</property>
</object>
<object class="GtkAdjustment" id="animate_appicon_hover_options_extent_adjustment">
<property name="lower">1</property>
<property name="upper">10</property>
<property name="step_increment">0.1</property>
<property name="page_increment">1</property>
</object>
<object class="GtkAdjustment" id="appicon_margin_adjustment">
<property name="lower">0.33</property>
<property name="upper">1</property>
@@ -7283,6 +7587,66 @@
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="animate_appicon_hover_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Animate hovering app icons</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="animate_appicon_hover_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkButton" id="animate_appicon_hover_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="valign">center</property>
<child>
<object class="GtkImage" id="animate_appicon_hover_options_image">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">emblem-system-symbolic</property>
</object>
</child>
<style>
<class name="circular"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSwitch" id="animate_appicon_hover_switch">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="halign">end</property>
<property name="valign">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
</object>
</child>
</object>

View File

@@ -194,6 +194,7 @@ var taskbarAppIcon = Utils.defineClass({
this._stateChangedId = 0;
}
this._onAnimateAppiconHoverChanged();
this._setAppIconPadding();
this._showDots();
@@ -230,6 +231,7 @@ var taskbarAppIcon = Utils.defineClass({
this._hoverChangeId = this.actor.connect('notify::hover', () => this._onAppIconHoverChanged());
this._dtpSettingsSignalIds = [
Me.settings.connect('changed::animate-appicon-hover', Lang.bind(this, this._onAnimateAppiconHoverChanged)),
Me.settings.connect('changed::dot-position', Lang.bind(this, this._settingsChangeRefresh)),
Me.settings.connect('changed::dot-size', Lang.bind(this, this._settingsChangeRefresh)),
Me.settings.connect('changed::dot-style-focused', Lang.bind(this, this._settingsChangeRefresh)),
@@ -269,6 +271,29 @@ var taskbarAppIcon = Utils.defineClass({
return this.app.create_icon_texture(this.dtpPanel.taskbar.iconSize);
},
// Used by TaskbarItemContainer to animate appIcons on hover
getCloneButton: function() {
// The source of the clone is this._container,
// using this.actor directly would break DnD style.
let clone = new Clutter.Clone({
source: this.actor.child,
x: this.actor.child.x, y: this.actor.child.y,
width: this.actor.child.width, height: this.actor.child.height,
pivot_point: new Utils.getPoint({ x: 0.5, y: 0.5 }),
opacity: 255,
reactive: false,
x_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.CENTER,
});
// "clone" of this.actor
return new St.Button({
child: clone,
x: this.actor.x, y: this.actor.y,
width: this.actor.width, height: this.actor.height,
reactive: false,
});
},
shouldShowTooltip: function() {
if (!Me.settings.get_boolean('show-tooltip') ||
(!this.isLauncher && Me.settings.get_boolean("show-window-previews") &&
@@ -378,6 +403,36 @@ var taskbarAppIcon = Utils.defineClass({
});
},
_onAnimateAppiconHoverChanged: function() {
if (Me.settings.get_boolean('animate-appicon-hover')) {
this._container.add_style_class_name('animate-appicon-hover');
// Workaround to prevent scaled icon from being ugly when it is animated on hover.
// It increases the "resolution" of the icon without changing the icon size.
this.icon.createIcon = (iconSize) => this.app.create_icon_texture(2 * iconSize);
this._iconIconBinActorAddedId = this.icon._iconBin.connect('actor-added', () => {
if (this.icon._iconBin.child.mapped) {
this.icon._iconBin.child.set_size(this.icon.iconSize, this.icon.iconSize);
} else {
let iconMappedId = this.icon._iconBin.child.connect('notify::mapped', () => {
this.icon._iconBin.child.set_size(this.icon.iconSize, this.icon.iconSize);
this.icon._iconBin.child.disconnect(iconMappedId);
});
}
});
if (this.icon._iconBin.child)
this.icon._createIconTexture(this.icon.iconSize);
} else {
this._container.remove_style_class_name('animate-appicon-hover');
if (this._iconIconBinActorAddedId) {
this.icon._iconBin.disconnect(this._iconIconBinActorAddedId);
this._iconIconBinActorAddedId = 0;
this.icon.createIcon = Lang.bind(this, this._createIcon);
}
}
},
_onMouseScroll: function(actor, event) {
let scrollAction = Me.settings.get_string('scroll-icon-action');
@@ -1553,6 +1608,10 @@ function ItemShowLabel() {
let position = this._dtpPanel.getPosition();
let labelOffset = node.get_length('-x-offset');
// From TaskbarItemContainer
if (this._getIconAnimationOffset)
labelOffset += this._getIconAnimationOffset();
let xOffset = Math.floor((itemWidth - labelWidth) / 2);
let x = stageX + xOffset
let y = stageY + (itemHeight - labelHeight) * .5;

127
prefs.js
View File

@@ -1988,14 +1988,104 @@ const Settings = new Lang.Class({
}
this._settings.bind('animate-app-switch',
this._builder.get_object('animate_app_switch_switch'),
'active',
Gio.SettingsBindFlags.DEFAULT);
this._builder.get_object('animate_app_switch_switch'),
'active',
Gio.SettingsBindFlags.DEFAULT);
this._settings.bind('animate-window-launch',
this._builder.get_object('animate_window_launch_switch'),
'active',
Gio.SettingsBindFlags.DEFAULT);
this._builder.get_object('animate_window_launch_switch'),
'active',
Gio.SettingsBindFlags.DEFAULT);
this._settings.bind('animate-appicon-hover',
this._builder.get_object('animate_appicon_hover_switch'),
'active',
Gio.SettingsBindFlags.DEFAULT);
this._settings.bind('animate-appicon-hover',
this._builder.get_object('animate_appicon_hover_button'),
'sensitive',
Gio.SettingsBindFlags.DEFAULT);
{
this._settings.bind('animate-appicon-hover-animation-type',
this._builder.get_object('animate_appicon_hover_options_type_combo'),
'active-id',
Gio.SettingsBindFlags.DEFAULT);
let scales = [
['animate_appicon_hover_options_duration_scale', 'animate-appicon-hover-animation-duration', 1],
['animate_appicon_hover_options_rotation_scale', 'animate-appicon-hover-animation-rotation', 1],
['animate_appicon_hover_options_travel_scale', 'animate-appicon-hover-animation-travel', 100],
['animate_appicon_hover_options_zoom_scale', 'animate-appicon-hover-animation-zoom', 100],
['animate_appicon_hover_options_convexity_scale', 'animate-appicon-hover-animation-convexity', 1],
['animate_appicon_hover_options_extent_scale', 'animate-appicon-hover-animation-extent', 1],
];
let updateScale = scale => {
let [id, key, factor] = scale;
let type = this._settings.get_string('animate-appicon-hover-animation-type');
let value = this._settings.get_value(key).deep_unpack()[type];
let defaultValue = this._settings.get_default_value(key).deep_unpack()[type];
this._builder.get_object(id).sensitive = defaultValue !== undefined;
this._builder.get_object(id).set_value(value * factor || 0);
this._builder.get_object(id).clear_marks();
this._builder.get_object(id).add_mark(defaultValue * factor, Gtk.PositionType.TOP,
defaultValue !== undefined ? (defaultValue * factor).toString() : ' ');
};
scales.forEach(scale => {
let [id, key, factor] = scale;
this._settings.connect('changed::' + key, () => updateScale(scale));
this._builder.get_object(id).connect('value-changed', widget => {
let type = this._settings.get_string('animate-appicon-hover-animation-type');
let variant = this._settings.get_value(key);
let unpacked = variant.deep_unpack();
if (unpacked[type] != widget.get_value() / factor) {
unpacked[type] = widget.get_value() / factor;
this._settings.set_value(key, new GLib.Variant(variant.get_type_string(), unpacked));
}
});
});
this._settings.connect('changed::animate-appicon-hover-animation-type', () => scales.forEach(updateScale));
scales.forEach(updateScale);
}
this._builder.get_object('animate_appicon_hover_button').connect('clicked', Lang.bind(this, function() {
let dialog = new Gtk.Dialog({ title: _('App icon animation options'),
transient_for: this.widget.get_toplevel(),
use_header_bar: true,
modal: true });
// GTK+ leaves positive values for application-defined response ids.
// Use +1 for the reset action
dialog.add_button(_('Reset to defaults'), 1);
let box = this._builder.get_object('animate_appicon_hover_options');
dialog.get_content_area().add(box);
dialog.connect('response', Lang.bind(this, function(dialog, id) {
if (id == 1) {
// restore default settings
this._settings.set_value('animate-appicon-hover-animation-type', this._settings.get_default_value('animate-appicon-hover-animation-type'));
this._settings.set_value('animate-appicon-hover-animation-duration', this._settings.get_default_value('animate-appicon-hover-animation-duration'));
this._settings.set_value('animate-appicon-hover-animation-rotation', this._settings.get_default_value('animate-appicon-hover-animation-rotation'));
this._settings.set_value('animate-appicon-hover-animation-travel', this._settings.get_default_value('animate-appicon-hover-animation-travel'));
this._settings.set_value('animate-appicon-hover-animation-zoom', this._settings.get_default_value('animate-appicon-hover-animation-zoom'));
this._settings.set_value('animate-appicon-hover-animation-convexity', this._settings.get_default_value('animate-appicon-hover-animation-convexity'));
this._settings.set_value('animate-appicon-hover-animation-extent', this._settings.get_default_value('animate-appicon-hover-animation-extent'));
} else {
// remove the settings box so it doesn't get destroyed;
dialog.get_content_area().remove(box);
dialog.destroy();
}
return;
}));
dialog.show_all();
}));
this._settings.bind('stockgs-keep-dash',
this._builder.get_object('stockgs_dash_switch'),
@@ -2124,7 +2214,30 @@ const Settings = new Lang.Class({
* Object containing all signals defined in the glade file
*/
_SignalHandler: {
animate_appicon_hover_options_duration_scale_format_value_cb: function(scale, value) {
return _("%d ms").format(value);
},
animate_appicon_hover_options_rotation_scale_format_value_cb: function(scale, value) {
return _("%d °").format(value);
},
animate_appicon_hover_options_travel_scale_format_value_cb: function(scale, value) {
return _("%d %%").format(value);
},
animate_appicon_hover_options_zoom_scale_format_value_cb: function(scale, value) {
return _("%d %%").format(value);
},
animate_appicon_hover_options_convexity_scale_format_value_cb: function(scale, value) {
return _("%.1f").format(value);
},
animate_appicon_hover_options_extent_scale_format_value_cb: function(scale, value) {
return Gettext.ngettext("%d icon", "%d icons", value).format(value);
},
position_bottom_button_clicked_cb: function(button) {
if (!this._ignorePositionRadios && button.get_active()) this._setPanelPosition(Pos.BOTTOM);
},

View File

@@ -58,6 +58,11 @@
<value value='1' nick='NUM_KEYPAD'/>
<value value='2' nick='BOTH'/>
</enum>
<enum id='org.gnome.shell.extensions.dash-to-panel.appIconHoverAnimationType'>
<value value='0' nick='SIMPLE'/>
<value value='1' nick='RIPPLE'/>
<value value='2' nick='PLANK'/>
</enum>
<schema path="/org/gnome/shell/extensions/dash-to-panel/" id="org.gnome.shell.extensions.dash-to-panel">
<key type="b" name="check-update">
<default>false</default>
@@ -763,6 +768,38 @@
<default>true</default>
<summary>Animate when new window launched</summary>
</key>
<key type="b" name="animate-appicon-hover">
<default>false</default>
<summary>Animate app icon on hover</summary>
</key>
<key name="animate-appicon-hover-animation-type" enum="org.gnome.shell.extensions.dash-to-panel.appIconHoverAnimationType">
<default>'SIMPLE'</default>
<summary>App icon hover animation type</summary>
</key>
<key type="a{sd}" name="animate-appicon-hover-animation-convexity">
<default>{'RIPPLE':2,'PLANK':1}</default>
<summary>App icon hover animation curve convexity (1 is linear, more is convex, less is concave)</summary>
</key>
<key type="a{su}" name="animate-appicon-hover-animation-duration">
<default>{'SIMPLE':160,'RIPPLE':130,'PLANK':100}</default>
<summary>App icon hover animation duration in milliseconds</summary>
</key>
<key type="a{si}" name="animate-appicon-hover-animation-extent">
<default>{'RIPPLE':4,'PLANK':4}</default>
<summary>App icon hover animation extent (maximum number of animated icons)</summary>
</key>
<key type="a{si}" name="animate-appicon-hover-animation-rotation">
<default>{'SIMPLE':0,'RIPPLE':10,'PLANK':0}</default>
<summary>App icon hover animation rotation in degrees</summary>
</key>
<key type="a{sd}" name="animate-appicon-hover-animation-travel">
<default>{'SIMPLE':0.30,'RIPPLE':0.40,'PLANK':0}</default>
<summary>App icon hover animation travel translation in relation to the app icon size</summary>
</key>
<key type="a{sd}" name="animate-appicon-hover-animation-zoom">
<default>{'SIMPLE':1,'RIPPLE':1.25,'PLANK':2}</default>
<summary>App icon hover animation zoom scale in relation to the app icon size</summary>
</key>
<key type="b" name="secondarymenu-contains-appmenu">
<default>true</default>
<summary>Integrate items from the gnome appmenu into the right click menu</summary>

View File

@@ -50,6 +50,10 @@
background-color: rgba(238, 238, 236, 0.1);
}
#dashtopanelScrollview .app-well-app:hover .dtp-container.animate-appicon-hover {
background: none;
}
#dashtopanelScrollview .app-well-app:active .dtp-container {
background-color: rgba(238, 238, 236, 0.18);
}

View File

@@ -26,6 +26,7 @@ const Clutter = imports.gi.Clutter;
const Config = imports.misc.config;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Signals = imports.signals;
const Lang = imports.lang;
@@ -48,6 +49,7 @@ const AppIcons = Me.imports.appIcons;
const Panel = Me.imports.panel;
const PanelManager = Me.imports.panelManager;
const PanelSettings = Me.imports.panelSettings;
const Pos = Me.imports.panelPositions;
const Utils = Me.imports.utils;
const WindowPreview = Me.imports.windowPreview;
@@ -68,6 +70,44 @@ function extendDashItemContainer(dashItemContainer) {
dashItemContainer.showLabel = AppIcons.ItemShowLabel;
};
const iconAnimationSettings = {
_getDictValue: function(key) {
let type = Me.settings.get_string('animate-appicon-hover-animation-type');
return Me.settings.get_value(key).deep_unpack()[type] || 0;
},
get type() {
if (!Me.settings.get_boolean('animate-appicon-hover'))
return "";
return Me.settings.get_string('animate-appicon-hover-animation-type');
},
get convexity() {
return Math.max(0, this._getDictValue('animate-appicon-hover-animation-convexity'));
},
get duration() {
return this._getDictValue('animate-appicon-hover-animation-duration');
},
get extent() {
return Math.max(1, this._getDictValue('animate-appicon-hover-animation-extent'));
},
get rotation() {
return this._getDictValue('animate-appicon-hover-animation-rotation');
},
get travel() {
return Math.max(0, this._getDictValue('animate-appicon-hover-animation-travel'));
},
get zoom() {
return Math.max(1, this._getDictValue('animate-appicon-hover-animation-zoom'));
},
};
/* This class is a fork of the upstream DashActor class (ui.dash.js)
*
* Summary of changes:
@@ -189,7 +229,9 @@ var taskbar = Utils.defineClass({
vscrollbar_policy: Gtk.PolicyType.NEVER,
enable_mouse_scrolling: true });
this._scrollView.connect('scroll-event', Lang.bind(this, this._onScrollEvent ));
this._scrollView.connect('leave-event', Lang.bind(this, this._onLeaveEvent));
this._scrollView.connect('motion-event', Lang.bind(this, this._onMotionEvent));
this._scrollView.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this._scrollView.add_actor(this._box);
this._showAppsIconWrapper = panel.showAppsIconWrapper;
@@ -370,6 +412,74 @@ var taskbar = Utils.defineClass({
this._disconnectWorkspaceSignals();
},
_dropIconAnimations: function() {
this._getTaskbarIcons().forEach(item => {
item.raise(0);
item.stretch(0);
});
},
_updateIconAnimations: function(pointerX, pointerY) {
this._iconAnimationTimestamp = Date.now();
let type = iconAnimationSettings.type;
if (!pointerX || !pointerY)
[pointerX, pointerY] = global.get_pointer();
this._getTaskbarIcons().forEach(item => {
let [x, y] = item.get_transformed_position();
let [width, height] = item.get_transformed_size();
let [centerX, centerY] = [x + width / 2, y + height / 2];
let size = this._box.vertical ? height : width;
let difference = this._box.vertical ? pointerY - centerY : pointerX - centerX;
let distance = Math.abs(difference);
let maxDistance = (iconAnimationSettings.extent / 2) * size;
if (type == 'PLANK') {
// Make the position stable for items that are far from the pointer.
let translation = distance <= maxDistance ?
distance / (2 + 8 * distance / maxDistance) :
// the previous expression with distance = maxDistance
maxDistance / 10;
if (difference > 0)
translation *= -1;
item.stretch(translation);
}
if (distance <= maxDistance) {
let level = (maxDistance - distance) / maxDistance;
level = Math.pow(level, iconAnimationSettings.convexity);
item.raise(level);
} else {
item.raise(0);
}
});
},
_onLeaveEvent: function(actor) {
let [stageX, stageY] = global.get_pointer();
let [success, x, y] = actor.transform_stage_point(stageX, stageY);
if (success && !actor.allocation.contains(x, y) && (iconAnimationSettings.type == 'RIPPLE' || iconAnimationSettings.type == 'PLANK'))
this._dropIconAnimations();
return Clutter.EVENT_PROPAGATE;
},
_onMotionEvent: function(actor_, event) {
if (iconAnimationSettings.type == 'RIPPLE' || iconAnimationSettings.type == 'PLANK') {
let timestamp = Date.now();
if (!this._iconAnimationTimestamp ||
(timestamp - this._iconAnimationTimestamp >= iconAnimationSettings.duration / 2)) {
let [pointerX, pointerY] = event.get_coords();
this._updateIconAnimations(pointerX, pointerY);
}
}
return Clutter.EVENT_PROPAGATE;
},
_onScrollEvent: function(actor, event) {
let orientation = this.dtpPanel.getOrientation();
@@ -566,6 +676,7 @@ var taskbar = Utils.defineClass({
Lang.bind(this, function() {
appIcon.actor.opacity = 50;
appIcon.isDragged = 1;
this._dropIconAnimations();
}));
appIcon._draggable.connect('drag-end',
Lang.bind(this, function() {
@@ -580,7 +691,7 @@ var taskbar = Utils.defineClass({
this._itemMenuStateChanged(item, opened);
}));
let item = new Dash.DashItemContainer();
let item = new TaskbarItemContainer();
item._dtpPanel = this.dtpPanel
extendDashItemContainer(item);
@@ -595,11 +706,19 @@ var taskbar = Utils.defineClass({
this._ensureAppIconVisibilityTimeoutId = 0;
return GLib.SOURCE_REMOVE;
}));
if (!appIcon.isDragged && iconAnimationSettings.type == 'SIMPLE')
appIcon.actor.get_parent().raise(1);
else if (!appIcon.isDragged && (iconAnimationSettings.type == 'RIPPLE' || iconAnimationSettings.type == 'PLANK'))
this._updateIconAnimations();
} else {
if (this._ensureAppIconVisibilityTimeoutId>0) {
Mainloop.source_remove(this._ensureAppIconVisibilityTimeoutId);
this._ensureAppIconVisibilityTimeoutId = 0;
}
if (!appIcon.isDragged && iconAnimationSettings.type == 'SIMPLE')
appIcon.actor.get_parent().raise(0);
}
}));
@@ -672,6 +791,14 @@ var taskbar = Utils.defineClass({
// add a custom signal to the appIcon, since gnome 3.8 the signal
// calling this callback was added upstream.
this.emit('menu-closed');
// The icon menu grabs the events and, once it is closed, the pointer is maybe
// no longer over the taskbar and the animations are not dropped.
if (iconAnimationSettings.type == 'RIPPLE' || iconAnimationSettings.type == 'PLANK') {
this._scrollView.sync_hover();
if (!this._scrollView.hover)
this._dropIconAnimations();
}
}
},
@@ -1230,6 +1357,187 @@ var taskbar = Utils.defineClass({
Signals.addSignalMethods(taskbar.prototype);
const CloneContainerConstraint = Utils.defineClass({
Name: 'DashToPanel-CloneContainerConstraint',
Extends: Clutter.BindConstraint,
vfunc_update_allocation: function(actor, actorBox) {
if (!this.source)
return;
let [stageX, stageY] = this.source.get_transformed_position();
let [width, height] = this.source.get_transformed_size();
actorBox.set_origin(stageX, stageY);
actorBox.set_size(width, height);
},
});
const TaskbarItemContainer = Utils.defineClass({
Name: 'DashToPanel-TaskbarItemContainer',
Extends: Dash.DashItemContainer,
// In case appIcon is removed from the taskbar while it is hovered,
// restore opacity before dashItemContainer.animateOutAndDestroy does the destroy animation.
animateOutAndDestroy: function() {
if (this._raisedClone) {
this._raisedClone.source.opacity = 255;
this._raisedClone.destroy();
}
this.callParent('animateOutAndDestroy');
},
// For ItemShowLabel
_getIconAnimationOffset: function() {
if (!Me.settings.get_boolean('animate-appicon-hover'))
return 0;
let travel = iconAnimationSettings.travel;
let zoom = iconAnimationSettings.zoom;
return this._dtpPanel.dtpSize * (travel + (zoom - 1) / 2);
},
_updateCloneContainerPosition: function(cloneContainer) {
let [stageX, stageY] = this.get_transformed_position();
if (Config.PACKAGE_VERSION >= '3.36')
cloneContainer.set_position(stageX - this.translation_x, stageY - this.translation_y);
else
cloneContainer.set_position(stageX, stageY);
},
_createRaisedClone: function() {
let [width, height] = this.get_transformed_size();
// "clone" of this child (appIcon actor)
let cloneButton = this.child._delegate.getCloneButton();
// "clone" of this (taskbarItemContainer)
let cloneContainer = new St.Bin({
child: cloneButton,
width: width, height: height,
reactive: false,
});
this._updateCloneContainerPosition(cloneContainer);
// For the stretch animation
if (Config.PACKAGE_VERSION >= '3.36') {
let boundProperty = this._dtpPanel.checkIfVertical() ? 'translation_y' : 'translation_x';
this.bind_property(boundProperty, cloneContainer, boundProperty, GObject.BindingFlags.SYNC_CREATE);
} else {
let constraint = new CloneContainerConstraint({ source: this });
cloneContainer.add_constraint(constraint);
}
// The clone follows its source when the taskbar is scrolled.
let taskbarScrollView = this.get_parent().get_parent();
let adjustment = this._dtpPanel.checkIfVertical() ? taskbarScrollView.vscroll.get_adjustment() : taskbarScrollView.hscroll.get_adjustment();
let adjustmentChangedId = adjustment.connect('notify::value', () => this._updateCloneContainerPosition(cloneContainer));
// Update clone position when an item is added to / removed from the taskbar.
let taskbarBox = this.get_parent();
let taskbarBoxAllocationChangedId = taskbarBox.connect('notify::allocation', () => this._updateCloneContainerPosition(cloneContainer));
// The clone itself
this._raisedClone = cloneButton.child;
this._raisedClone.connect('destroy', () => {
adjustment.disconnect(adjustmentChangedId);
taskbarBox.disconnect(taskbarBoxAllocationChangedId);
Mainloop.idle_add(() => cloneContainer.destroy());
delete this._raisedClone;
});
this._raisedClone.source.opacity = 0;
Main.uiGroup.add_actor(cloneContainer);
},
// Animate the clone.
// AppIcon actors cannot go outside the taskbar so the animation is done with a clone.
// If level is zero, the clone is dropped and destroyed.
raise: function(level) {
if (this._raisedClone)
Utils.stopAnimations(this._raisedClone);
else if (level)
this._createRaisedClone();
else
return;
let panelPosition = this._dtpPanel.getPosition();
let panelElementPositions = this._dtpPanel.panelManager.panelsElementPositions[this._dtpPanel.monitor.index] || Pos.defaults;
let taskbarPosition = panelElementPositions.filter(pos => pos.element == 'taskbar')[0].position;
let vertical = panelPosition == St.Side.LEFT || panelPosition == St.Side.RIGHT;
let translationDirection = panelPosition == St.Side.TOP || panelPosition == St.Side.LEFT ? 1 : -1;
let rotationDirection;
if (panelPosition == St.Side.LEFT || taskbarPosition == Pos.STACKED_TL)
rotationDirection = -1;
else if (panelPosition == St.Side.RIGHT || taskbarPosition == Pos.STACKED_BR)
rotationDirection = 1;
else {
let items = this.get_parent().get_children();
let index = items.indexOf(this);
rotationDirection = (index - (items.length - 1) / 2) / ((items.length - 1) / 2);
}
let duration = iconAnimationSettings.duration / 1000;
let rotation = iconAnimationSettings.rotation;
let travel = iconAnimationSettings.travel;
let zoom = iconAnimationSettings.zoom;
// level is about 1 for the icon that is hovered, less for others.
// time depends on the translation to do.
let [width, height] = this._raisedClone.source.get_transformed_size();
let translationMax = (vertical ? width : height) * (travel + (zoom - 1) / 2);
let translationEnd = translationMax * level;
let translationDone = vertical ? this._raisedClone.translation_x : this._raisedClone.translation_y;
let translationTodo = Math.abs(translationEnd - translationDone);
let scale = 1 + (zoom - 1) * level;
let rotationAngleZ = rotationDirection * rotation * level;
let time = duration * translationTodo / translationMax;
let options = {
scale_x: scale, scale_y: scale,
rotation_angle_z: rotationAngleZ,
time: time,
transition: 'easeOutQuad',
onComplete: () => {
if (!level) {
this._raisedClone.source.opacity = 255;
this._raisedClone.destroy();
delete this._raisedClone;
}
},
};
options[vertical ? 'translation_x' : 'translation_y'] = translationDirection * translationEnd;
Utils.animate(this._raisedClone, options);
},
// Animate this and cloneContainer, since cloneContainer translation is bound to this.
stretch: function(translation) {
let duration = iconAnimationSettings.duration / 1000;
let zoom = iconAnimationSettings.zoom;
let animatedProperty = this._dtpPanel.checkIfVertical() ? 'translation_y' : 'translation_x';
let isShowing = this.opacity != 255 || this.child.opacity != 255;
if (isShowing) {
// Do no stop the animation initiated in DashItemContainer.show.
this[animatedProperty] = zoom * translation;
} else {
let options = {
time: duration,
transition: 'easeOutQuad',
};
options[animatedProperty] = zoom * translation;
Utils.stopAnimations(this);
Utils.animate(this, options);
}
},
});
var DragPlaceholderItem = Utils.defineClass({
Name: 'DashToPanel-DragPlaceholderItem',
Extends: St.Widget,