notifications: Highlight expanded group by fading other groups

This commit is contained in:
Julian Sparber 2023-12-04 12:00:03 +01:00
parent a6aac285b9
commit 22f1c8acba
2 changed files with 173 additions and 1 deletions

View file

@ -760,6 +760,125 @@ export const Calendar = GObject.registerClass({
}
});
const FadeEffect = GObject.registerClass({
Properties: {
'top-fade': GObject.ParamSpec.float(
'top-fade', 'top-fade', 'top-fade',
GObject.ParamFlags.READWRITE,
0, GLib.MAXINT32, 0),
'bottom-fade': GObject.ParamSpec.float(
'bottom-fade', 'bottom-fade', 'bottom-fade',
GObject.ParamFlags.READWRITE,
0, GLib.MAXINT32, 0),
'opacity': GObject.ParamSpec.float(
'opacity', 'opacity', 'opacity',
GObject.ParamFlags.READWRITE,
0, 1, 0),
},
}, class FadeEffect extends Shell.GLSLEffect {
_init(params) {
super._init(params);
this._heightLocation = this.get_uniform_location('height');
this._widthLocation = this.get_uniform_location('width');
this._topFadeHeightLocation = this.get_uniform_location('top_fade_height');
this._bottomFadeHeightLocation = this.get_uniform_location('bottom_fade_height');
this._opacityLocation = this.get_uniform_location('opacity');
this._topFade = 0;
this._bottomFade = 0;
this._opacity = 0;
}
get opacity() {
return this._opacity;
}
set opacity(opacity) {
if (this._opacity === opacity)
return;
this._opacity = opacity;
this.set_uniform_float(this._opacityLocation, 1, [opacity]);
this.actor.queue_redraw();
this.notify('opacity');
}
get topFade() {
return this._topFade;
}
set topFade(height) {
if (this._topFade === height)
return;
this._topFade = height;
this.set_uniform_float(this._topFadeHeightLocation, 1, [height]);
this.actor.queue_redraw();
this.notify('top-fade');
}
get bottomFade() {
return this._bottomFade;
}
set bottomFade(height) {
if (this._bottomFade === height)
return;
this._bottomFade = height;
this.set_uniform_float(this._bottomFadeHeightLocation, 1, [height]);
this.actor.queue_redraw();
this.notify('bottom-fade');
}
vfunc_set_actor(actor) {
if (this.actor)
this.actor.disconnectObject(this);
if (actor) {
actor.connectObject('notify::allocation', () => {
this.set_uniform_float(this._heightLocation, 1, [this.get_actor().height]);
this.set_uniform_float(this._widthLocation, 1, [this.get_actor().width]);
}, this);
}
super.vfunc_set_actor(actor);
}
vfunc_build_pipeline() {
const dec = `uniform sampler2D tex; \n
uniform float height; \n
uniform float width; \n
uniform float opacity; \n
uniform float top_fade_height; \n
uniform float bottom_fade_height; \n`;
const src = `cogl_color_out = cogl_color_in * texture2D (tex, vec2 (cogl_tex_coord_in[0].xy)); \n
float base_fade = 200; \n
float y = height * cogl_tex_coord_in[0].y; \n
float y_from_bottom = height - y; \n
float ratio = 1.0; \n
if (y < top_fade_height) { \n
ratio = y / (top_fade_height + base_fade); \n
ratio = 1 - ((1 - ratio) * opacity); \n
} \n
if (y_from_bottom < bottom_fade_height) { \n
ratio = y_from_bottom / (bottom_fade_height + base_fade); \n
ratio = 1 - ((1 - ratio) * opacity); \n
} \n
cogl_color_out *= ratio; \n`;
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, dec, src, true);
}
});
const Placeholder = GObject.registerClass(
class Placeholder extends St.BoxLayout {
_init() {
@ -818,6 +937,7 @@ class CalendarMessageList extends St.Widget {
this._scrollView = new St.ScrollView({
style_class: 'vfade',
effect: new FadeEffect({name: 'highlight'}),
overlay_scrollbars: true,
x_expand: true, y_expand: true,
child: this._messageView,

View file

@ -1638,11 +1638,12 @@ export const MessageView = GObject.registerClass({
// Collapse the previously expanded group
if (prevGroup) {
delete this._firstMove;
await prevGroup.collapse();
scrollView.get_parent().layout_manager.frozen = false;
this._showScrollbar();
scrollView.style_class = 'vfade';
this._unhiglightGroup(prevGroup);
await prevGroup.collapse();
const duration = this.mapped ? MESSAGE_ANIMATION_TIME : 0;
this.vadjustment.ease(this._collapsedScrollPosition, {
@ -1658,6 +1659,7 @@ export const MessageView = GObject.registerClass({
this.set_child_below_sibling(this._overlay, group.get_parent());
this._overlay.show();
this._firstMove = true;
this._higlightGroup(group);
await group.expand();
scrollView.style_class = '';
} else {
@ -1792,6 +1794,56 @@ export const MessageView = GObject.registerClass({
});
}
_higlightGroup(group) {
const duration = this.mapped ? MESSAGE_ANIMATION_TIME : 0;
const scrollView = this.get_parent();
const effect = scrollView.get_effect('highlight');
this._highlightHandlerId = group.connect('notify::allocation', this._updateHighlight.bind(this, group));
this._valueHandlerId = this.vadjustment.connect('notify::value', this._updateHighlight.bind(this, group));
scrollView.ease_property('@effects.highlight.opacity', 1.0, {
progress_mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration,
});
effect.enabled = true;
this._updateHighlight(group);
}
_updateHighlight(group) {
const effect = this.get_parent().get_effect('highlight');
const {y1, y2} = group.getAllocationInParent(this);
const pageHeight = this.vadjustment.pageSize;
const value = this.vadjustment.value;
effect.topFade = Math.max(0, y1 - value);
effect.bottomFade = Math.max(0, pageHeight - ((y1 - value) + (y2 - y1)));
}
_unhiglightGroup(group) {
const duration = this.mapped ? MESSAGE_ANIMATION_TIME : 0;
const scrollView = this.get_parent();
const effect = scrollView.get_effect('highlight');
scrollView.ease_property('@effects.highlight.opacity', 0.0, {
progress_mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration,
onStopped: () => {
if (this._highlightHandlerId) {
group.disconnect(this._highlightHandlerId);
this._highlightHandlerId = undefined;
}
if (this._valueHandlerId) {
this.vadjustment.disconnect(this._valueHandlerId);
this._valueHandlerId = undefined;
}
effect.enabled = false;
},
});
}
_hideScrollbar() {
const duration = this.mapped ? MESSAGE_ANIMATION_TIME : 0;
const scrollView = this.get_parent();