cleanup: Use inheritance for Actor classes instead of composition

Remove the `this.actor = ...` and `this.actor._delegate = this` patterns in most
of classes, by inheriting all the actor container classes.

Uses interfaces when needed for making sure that multiple classes will implement
some required methods or to avoid redefining the same code multiple times.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
This commit is contained in:
Marco Trevisan (Treviño)
2019-07-16 11:24:13 +02:00
committed by Florian Müllner
parent f67b409fc1
commit c4c5c4fd5c
58 changed files with 2000 additions and 1757 deletions

View File

@ -1,8 +1,8 @@
/* exported MessageListSection */
const { Atk, Clutter, Gio, GLib,
GObject, Graphene, Meta, Pango, St } = imports.gi;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Signals = imports.signals;
const Calendar = imports.ui.calendar;
const Util = imports.misc.util;
@ -32,13 +32,18 @@ function _fixMarkup(text, allowMarkup) {
return GLib.markup_escape_text(text, -1);
}
var URLHighlighter = class URLHighlighter {
constructor(text = '', lineWrap, allowMarkup) {
this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter',
x_expand: true, x_align: Clutter.ActorAlign.START });
var URLHighlighter = GObject.registerClass(
class URLHighlighter extends St.Label {
_init(text = '', lineWrap, allowMarkup) {
super._init({
reactive: true,
style_class: 'url-highlighter',
x_expand: true,
x_align: Clutter.ActorAlign.START
});
this._linkColor = '#ccccff';
this.actor.connect('style-changed', () => {
let [hasColor, color] = this.actor.get_theme_node().lookup_color('link-color', false);
this.connect('style-changed', () => {
let [hasColor, color] = this.get_theme_node().lookup_color('link-color', false);
if (hasColor) {
let linkColor = color.to_string().substr(0, 7);
if (linkColor != this._linkColor) {
@ -47,11 +52,11 @@ var URLHighlighter = class URLHighlighter {
}
}
});
this.actor.clutter_text.line_wrap = lineWrap;
this.actor.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
this.clutter_text.line_wrap = lineWrap;
this.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
this.setMarkup(text, allowMarkup);
this.actor.connect('button-press-event', (actor, event) => {
this.connect('button-press-event', (actor, event) => {
// Don't try to URL highlight when invisible.
// The MessageTray doesn't actually hide us, so
// we need to check for paint opacities as well.
@ -63,7 +68,7 @@ var URLHighlighter = class URLHighlighter {
// handler, if an URL is clicked
return this._findUrlAtPos(event) != -1;
});
this.actor.connect('button-release-event', (actor, event) => {
this.connect('button-release-event', (actor, event) => {
if (!actor.visible || actor.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
@ -78,7 +83,7 @@ var URLHighlighter = class URLHighlighter {
}
return Clutter.EVENT_PROPAGATE;
});
this.actor.connect('motion-event', (actor, event) => {
this.connect('motion-event', (actor, event) => {
if (!actor.visible || actor.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
@ -92,8 +97,8 @@ var URLHighlighter = class URLHighlighter {
}
return Clutter.EVENT_PROPAGATE;
});
this.actor.connect('leave-event', () => {
if (!this.actor.visible || this.actor.get_paint_opacity() == 0)
this.connect('leave-event', () => {
if (!this.visible || this.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
if (this._cursorChanged) {
@ -108,9 +113,9 @@ var URLHighlighter = class URLHighlighter {
text = text ? _fixMarkup(text, allowMarkup) : '';
this._text = text;
this.actor.clutter_text.set_markup(text);
this.clutter_text.set_markup(text);
/* clutter_text.text contain text without markup */
this._urls = Util.findUrls(this.actor.clutter_text.text);
this._urls = Util.findUrls(this.clutter_text.text);
this._highlightUrls();
}
@ -126,16 +131,15 @@ var URLHighlighter = class URLHighlighter {
pos = url.pos + url.url.length;
}
markup += this._text.substr(pos);
this.actor.clutter_text.set_markup(markup);
this.clutter_text.set_markup(markup);
}
_findUrlAtPos(event) {
let success_;
let [x, y] = event.get_coords();
[success_, x, y] = this.actor.transform_stage_point(x, y);
[, x, y] = this.transform_stage_point(x, y);
let findPos = -1;
for (let i = 0; i < this.actor.clutter_text.text.length; i++) {
let [success_, px, py, lineHeight] = this.actor.clutter_text.position_to_coords(i);
for (let i = 0; i < this.clutter_text.text.length; i++) {
let [, px, py, lineHeight] = this.clutter_text.position_to_coords(i);
if (py > y || py + lineHeight < y || x < px)
continue;
findPos = i;
@ -148,7 +152,7 @@ var URLHighlighter = class URLHighlighter {
}
return -1;
}
};
});
var ScaleLayout = GObject.registerClass(
class ScaleLayout extends Clutter.BinLayout {
@ -284,21 +288,31 @@ var LabelExpanderLayout = GObject.registerClass({
}
});
var Message = class Message {
constructor(title, body) {
this.expanded = false;
var Message = GObject.registerClass({
GTypeName: 'MessageList_Message',
Signals: {
'close': {},
'expanded': {},
'unexpanded': {},
}
}, class Message extends St.Button {
_init(title, body) {
super._init({
style_class: 'message',
accessible_role: Atk.Role.NOTIFICATION,
can_focus: true,
x_expand: true,
x_fill: true
});
this.expanded = false;
this._useBodyMarkup = false;
this.actor = new St.Button({ style_class: 'message',
accessible_role: Atk.Role.NOTIFICATION,
can_focus: true,
x_expand: true, x_fill: true });
this.actor.connect('key-press-event',
this._onKeyPressed.bind(this));
this.connect('key-press-event', this._onKeyPressed.bind(this));
let vbox = new St.BoxLayout({ vertical: true });
this.actor.set_child(vbox);
this.set_child(vbox);
let hbox = new St.BoxLayout();
vbox.add_actor(hbox);
@ -342,15 +356,15 @@ var Message = class Message {
contentBox.add_actor(this._bodyStack);
this.bodyLabel = new URLHighlighter('', false, this._useBodyMarkup);
this.bodyLabel.actor.add_style_class_name('message-body');
this._bodyStack.add_actor(this.bodyLabel.actor);
this.bodyLabel.add_style_class_name('message-body');
this._bodyStack.add_actor(this.bodyLabel);
this.setBody(body);
this._closeButton.connect('clicked', this.close.bind(this));
let actorHoverId = this.actor.connect('notify::hover', this._sync.bind(this));
this._closeButton.connect('destroy', this.actor.disconnect.bind(this.actor, actorHoverId));
this.actor.connect('clicked', this._onClicked.bind(this));
this.actor.connect('destroy', this._onDestroy.bind(this));
let actorHoverId = this.connect('notify::hover', this._sync.bind(this));
this._closeButton.connect('destroy', this.disconnect.bind(this, actorHoverId));
this.connect('clicked', this._onClicked.bind(this));
this.connect('destroy', this._onDestroy.bind(this));
this._sync();
}
@ -436,7 +450,7 @@ var Message = class Message {
if (this._bodyStack.get_n_children() < 2) {
this._expandedLabel = new URLHighlighter(this._bodyText,
true, this._useBodyMarkup);
this.setExpandedBody(this._expandedLabel.actor);
this.setExpandedBody(this._expandedLabel);
}
if (animate) {
@ -489,7 +503,7 @@ var Message = class Message {
}
_sync() {
let visible = this.actor.hover && this.canClose();
let visible = this.hover && this.canClose();
this._closeButton.opacity = visible ? 255 : 0;
this._closeButton.reactive = visible;
}
@ -510,35 +524,61 @@ var Message = class Message {
}
return Clutter.EVENT_PROPAGATE;
}
};
Signals.addSignalMethods(Message.prototype);
});
var MessageListSection = class MessageListSection {
constructor() {
this.actor = new St.BoxLayout({ style_class: 'message-list-section',
clip_to_allocation: true,
x_expand: true, vertical: true });
var MessageListSection = GObject.registerClass({
Properties: {
'can-clear': GObject.ParamSpec.boolean(
'can-clear', 'can-clear', 'can-clear',
GObject.ParamFlags.READABLE,
false),
'empty': GObject.ParamSpec.boolean(
'empty', 'empty', 'empty',
GObject.ParamFlags.READABLE,
true),
},
Signals: {
'can-clear-changed': {},
'empty-changed': {},
'message-focused': { param_types: [Message.$gtype] },
}
}, class MessageListSection extends St.BoxLayout {
_init() {
super._init({
style_class: 'message-list-section',
clip_to_allocation: true,
vertical: true,
x_expand: true
});
this._list = new St.BoxLayout({ style_class: 'message-list-section-list',
vertical: true });
this.actor.add_actor(this._list);
this.add_actor(this._list);
this._list.connect('actor-added', this._sync.bind(this));
this._list.connect('actor-removed', this._sync.bind(this));
let id = Main.sessionMode.connect('updated',
this._sync.bind(this));
this.actor.connect('destroy', () => {
this.connect('destroy', () => {
Main.sessionMode.disconnect(id);
});
this._messages = new Map();
this._date = new Date();
this.empty = true;
this.canClear = false;
this._empty = true;
this._canClear = false;
this._sync();
}
get empty() {
return this._empty;
}
get canClear() {
return this._canClear;
}
_onKeyFocusIn(messageActor) {
this.emit('message-focused', messageActor);
}
@ -570,9 +610,9 @@ var MessageListSection = class MessageListSection {
obj.container = new St.Widget({ layout_manager: new ScaleLayout(),
pivot_point: pivot,
scale_x: scale, scale_y: scale });
obj.keyFocusId = message.actor.connect('key-focus-in',
obj.keyFocusId = message.connect('key-focus-in',
this._onKeyFocusIn.bind(this));
obj.destroyId = message.actor.connect('destroy', () => {
obj.destroyId = message.connect('destroy', () => {
this.removeMessage(message, false);
});
obj.closeId = message.connect('close', () => {
@ -580,7 +620,7 @@ var MessageListSection = class MessageListSection {
});
this._messages.set(message, obj);
obj.container.add_actor(message.actor);
obj.container.add_actor(message);
this._list.insert_child_at_index(obj.container, index);
@ -622,8 +662,8 @@ var MessageListSection = class MessageListSection {
removeMessage(message, animate) {
let obj = this._messages.get(message);
message.actor.disconnect(obj.destroyId);
message.actor.disconnect(obj.keyFocusId);
message.disconnect(obj.destroyId);
message.disconnect(obj.keyFocusId);
message.disconnect(obj.closeId);
this._messages.delete(message);
@ -672,33 +712,24 @@ var MessageListSection = class MessageListSection {
}
}
_canClear() {
for (let message of this._messages.keys())
if (message.canClose())
return true;
return false;
}
_shouldShow() {
return !this.empty;
}
_sync() {
let empty = this._list.get_n_children() == 0;
let changed = this.empty !== empty;
this.empty = empty;
if (changed)
this.emit('empty-changed');
if (this._empty != empty) {
this._empty = empty;
this.notify('empty');
}
let canClear = this._canClear();
changed = this.canClear !== canClear;
this.canClear = canClear;
let canClear = [...this._messages.keys()].some(m => m.canClose());
if (this._canClear != canClear) {
this._canClear = canClear;
this.notify('can-clear');
}
if (changed)
this.emit('can-clear-changed');
this.actor.visible = this.allowed && this._shouldShow();
this.visible = this.allowed && this._shouldShow();
}
};
Signals.addSignalMethods(MessageListSection.prototype);
});