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:

committed by
Florian Müllner

parent
f67b409fc1
commit
c4c5c4fd5c
@ -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);
|
||||
});
|
||||
|
Reference in New Issue
Block a user