2024-07-08 22:46:35 +02:00

310 lines
8.0 KiB
JavaScript
Executable File

// SPDX-FileCopyrightText: GSConnect Developers https://github.com/GSConnect
//
// SPDX-License-Identifier: GPL-2.0-or-later
import Clutter from 'gi://Clutter';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import Pango from 'gi://Pango';
import St from 'gi://St';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
/**
* An StTooltip for ClutterActors
*
* Adapted from: https://github.com/RaphaelRochet/applications-overview-tooltip
* See also: https://github.com/GNOME/gtk/blob/master/gtk/gtktooltip.c
*/
export let TOOLTIP_BROWSE_ID = 0;
export let TOOLTIP_BROWSE_MODE = false;
export default class Tooltip {
constructor(params) {
Object.assign(this, params);
this._bin = null;
this._hoverTimeoutId = 0;
this._showing = false;
this._destroyId = this.parent.connect(
'destroy',
this.destroy.bind(this)
);
this._hoverId = this.parent.connect(
'notify::hover',
this._onHover.bind(this)
);
this._buttonPressEventId = this.parent.connect(
'button-press-event',
this._hide.bind(this)
);
}
get custom() {
if (this._custom === undefined)
this._custom = null;
return this._custom;
}
set custom(actor) {
this._custom = actor;
this._markup = null;
this._text = null;
if (this._showing)
this._show();
}
get gicon() {
if (this._gicon === undefined)
this._gicon = null;
return this._gicon;
}
set gicon(gicon) {
this._gicon = gicon;
if (this._showing)
this._show();
}
get icon() {
return (this.gicon) ? this.gicon.name : null;
}
set icon(icon_name) {
if (!icon_name)
this.gicon = null;
else
this.gicon = new Gio.ThemedIcon({name: icon_name});
}
get markup() {
if (this._markup === undefined)
this._markup = null;
return this._markup;
}
set markup(text) {
this._markup = text;
this._text = null;
if (this._showing)
this._show();
}
get text() {
if (this._text === undefined)
this._text = null;
return this._text;
}
set text(text) {
this._markup = null;
this._text = text;
if (this._showing)
this._show();
}
get x_offset() {
if (this._x_offset === undefined)
this._x_offset = 0;
return this._x_offset;
}
set x_offset(offset) {
this._x_offset = (Number.isInteger(offset)) ? offset : 0;
}
get y_offset() {
if (this._y_offset === undefined)
this._y_offset = 0;
return this._y_offset;
}
set y_offset(offset) {
this._y_offset = (Number.isInteger(offset)) ? offset : 0;
}
_show() {
if (this.text === null && this.markup === null)
return this._hide();
if (this._bin === null) {
this._bin = new St.Bin({
style_class: 'osd-window gsconnect-tooltip',
opacity: 232,
});
if (this.custom) {
this._bin.child = this.custom;
} else {
this._bin.child = new St.BoxLayout({vertical: false});
if (this.gicon) {
this._bin.child.icon = new St.Icon({
gicon: this.gicon,
y_align: St.Align.START,
});
this._bin.child.icon.set_y_align(Clutter.ActorAlign.START);
this._bin.child.add_child(this._bin.child.icon);
}
this.label = new St.Label({text: this.markup || this.text});
this.label.clutter_text.line_wrap = true;
this.label.clutter_text.line_wrap_mode = Pango.WrapMode.WORD;
this.label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this.label.clutter_text.use_markup = (this.markup);
this._bin.child.add_child(this.label);
}
Main.layoutManager.uiGroup.add_child(this._bin);
Main.layoutManager.uiGroup.set_child_above_sibling(this._bin, null);
} else if (this.custom) {
this._bin.child = this.custom;
} else {
if (this._bin.child.icon)
this._bin.child.icon.destroy();
if (this.gicon) {
this._bin.child.icon = new St.Icon({gicon: this.gicon});
this._bin.child.insert_child_at_index(this._bin.child.icon, 0);
}
this.label.clutter_text.text = this.markup || this.text;
this.label.clutter_text.use_markup = (this.markup);
}
// Position tooltip
let [x, y] = this.parent.get_transformed_position();
x = (x + (this.parent.width / 2)) - Math.round(this._bin.width / 2);
x += this.x_offset;
y += this.y_offset;
// Show tooltip
if (this._showing) {
this._bin.ease({
x: x,
y: y,
time: 0.15,
transition: Clutter.AnimationMode.EASE_OUT_QUAD,
});
} else {
this._bin.set_position(x, y);
this._bin.ease({
opacity: 232,
time: 0.15,
transition: Clutter.AnimationMode.EASE_OUT_QUAD,
});
this._showing = true;
}
// Enable browse mode
TOOLTIP_BROWSE_MODE = true;
if (TOOLTIP_BROWSE_ID) {
GLib.source_remove(TOOLTIP_BROWSE_ID);
TOOLTIP_BROWSE_ID = 0;
}
if (this._hoverTimeoutId) {
GLib.source_remove(this._hoverTimeoutId);
this._hoverTimeoutId = 0;
}
}
_hide() {
if (this._bin) {
this._bin.ease({
opacity: 0,
time: 0.10,
transition: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
Main.layoutManager.uiGroup.remove_actor(this._bin);
if (this.custom)
this._bin.remove_child(this.custom);
this._bin.destroy();
this._bin = null;
},
});
}
TOOLTIP_BROWSE_ID = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
TOOLTIP_BROWSE_MODE = false;
TOOLTIP_BROWSE_ID = 0;
return false;
});
if (this._hoverTimeoutId) {
GLib.source_remove(this._hoverTimeoutId);
this._hoverTimeoutId = 0;
}
this._showing = false;
this._hoverTimeoutId = 0;
}
_onHover() {
if (this.parent.hover) {
if (!this._hoverTimeoutId) {
if (this._showing) {
this._show();
} else {
this._hoverTimeoutId = GLib.timeout_add(
GLib.PRIORITY_DEFAULT,
(TOOLTIP_BROWSE_MODE) ? 60 : 500,
() => {
this._show();
this._hoverTimeoutId = 0;
return false;
}
);
}
}
} else {
this._hide();
}
}
destroy() {
this.parent.disconnect(this._destroyId);
this.parent.disconnect(this._hoverId);
this.parent.disconnect(this._buttonPressEventId);
if (this.custom)
this.custom.destroy();
if (this._bin) {
Main.layoutManager.uiGroup.remove_actor(this._bin);
this._bin.destroy();
}
if (TOOLTIP_BROWSE_ID) {
GLib.source_remove(TOOLTIP_BROWSE_ID);
TOOLTIP_BROWSE_ID = 0;
}
if (this._hoverTimeoutId) {
GLib.source_remove(this._hoverTimeoutId);
this._hoverTimeoutId = 0;
}
}
}