dateMenu: Add "Events" section
Events have a clear and obvious connection to the calendar, and similar to the Clocks and Weather sections there's a strong link to a particular application. Adding them as another section to the right-hand side of the calendar therefore presents a viable alternative to the old events section. https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1282
This commit is contained in:
parent
771050f4d7
commit
fdd9def922
@ -177,6 +177,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Events */
|
||||||
|
.events-button {
|
||||||
|
@include notification_bubble;
|
||||||
|
padding: $base_padding * 2;
|
||||||
|
|
||||||
|
.events-box {
|
||||||
|
spacing: $base_spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.events-list {
|
||||||
|
spacing: 2 * $base_spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.events-title {
|
||||||
|
color: desaturate(darken($fg_color,40%), 10%);
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: $base_margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-time {
|
||||||
|
color: darken($fg_color,20%);
|
||||||
|
font-feature-settings: "tnum";
|
||||||
|
@include fontsize($base_font_size - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* World clocks */
|
/* World clocks */
|
||||||
.world-clocks-button {
|
.world-clocks-button {
|
||||||
@include notification_bubble;
|
@include notification_bubble;
|
||||||
|
@ -13,7 +13,11 @@ const System = imports.system;
|
|||||||
|
|
||||||
const { loadInterfaceXML } = imports.misc.fileUtils;
|
const { loadInterfaceXML } = imports.misc.fileUtils;
|
||||||
|
|
||||||
|
const NC_ = (context, str) => '%s\u0004%s'.format(context, str);
|
||||||
|
const T_ = Shell.util_translate_time_string;
|
||||||
|
|
||||||
const MAX_FORECASTS = 5;
|
const MAX_FORECASTS = 5;
|
||||||
|
const ELLIPSIS_CHAR = '\u2026';
|
||||||
|
|
||||||
const ClocksIntegrationIface = loadInterfaceXML('org.gnome.Shell.ClocksIntegration');
|
const ClocksIntegrationIface = loadInterfaceXML('org.gnome.Shell.ClocksIntegration');
|
||||||
const ClocksProxy = Gio.DBusProxy.makeProxyWrapper(ClocksIntegrationIface);
|
const ClocksProxy = Gio.DBusProxy.makeProxyWrapper(ClocksIntegrationIface);
|
||||||
@ -84,6 +88,188 @@ class TodayButton extends St.Button {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var EventsSection = GObject.registerClass(
|
||||||
|
class EventsSection extends St.Button {
|
||||||
|
_init() {
|
||||||
|
super._init({
|
||||||
|
style_class: 'events-button',
|
||||||
|
can_focus: true,
|
||||||
|
x_expand: true,
|
||||||
|
child: new St.BoxLayout({
|
||||||
|
style_class: 'events-box',
|
||||||
|
vertical: true,
|
||||||
|
x_expand: true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
this._startDate = null;
|
||||||
|
this._endDate = null;
|
||||||
|
|
||||||
|
this._eventSource = null;
|
||||||
|
this._calendarApp = null;
|
||||||
|
|
||||||
|
this._title = new St.Label({
|
||||||
|
style_class: 'events-title',
|
||||||
|
});
|
||||||
|
this.child.add_child(this._title);
|
||||||
|
|
||||||
|
this._eventsList = new St.BoxLayout({
|
||||||
|
style_class: 'events-list',
|
||||||
|
vertical: true,
|
||||||
|
x_expand: true,
|
||||||
|
});
|
||||||
|
this.child.add_child(this._eventsList);
|
||||||
|
|
||||||
|
this._appSys = Shell.AppSystem.get_default();
|
||||||
|
this._appSys.connect('installed-changed',
|
||||||
|
this._appInstalledChanged.bind(this));
|
||||||
|
this._appInstalledChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
setDate(date) {
|
||||||
|
const day = [date.getFullYear(), date.getMonth(), date.getDate()];
|
||||||
|
this._startDate = new Date(...day);
|
||||||
|
this._endDate = new Date(...day, 23, 59, 59, 999);
|
||||||
|
|
||||||
|
this._updateTitle();
|
||||||
|
this._reloadEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
setEventSource(eventSource) {
|
||||||
|
if (!(eventSource instanceof Calendar.EventSourceBase))
|
||||||
|
throw new Error('Event source is not valid type');
|
||||||
|
|
||||||
|
this._eventSource = eventSource;
|
||||||
|
this._eventSource.connect('changed', this._reloadEvents.bind(this));
|
||||||
|
this._eventSource.connect('notify::has-calendars',
|
||||||
|
this._sync.bind(this));
|
||||||
|
this._sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateTitle() {
|
||||||
|
/* Translators: Shown on calendar heading when selected day occurs on current year */
|
||||||
|
const sameYearFormat = T_(NC_('calendar heading', '%B %-d'));
|
||||||
|
|
||||||
|
/* Translators: Shown on calendar heading when selected day occurs on different year */
|
||||||
|
const otherYearFormat = T_(NC_('calendar heading', '%B %-d %Y'));
|
||||||
|
|
||||||
|
const timeSpanDay = GLib.TIME_SPAN_DAY / 1000;
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
if (this._startDate <= now && now <= this._endDate)
|
||||||
|
this._title.text = _('Today');
|
||||||
|
else if (this._endDate < now && now - this._endDate < timeSpanDay)
|
||||||
|
this._title.text = _('Yesterday');
|
||||||
|
else if (this._startDate > now && this._startDate - now < timeSpanDay)
|
||||||
|
this._title.text = _('Tomorrow');
|
||||||
|
else if (this._startDate.getFullYear() === now.getFullYear())
|
||||||
|
this._title.text = this._startDate.toLocaleFormat(sameYearFormat);
|
||||||
|
else
|
||||||
|
this._title.text = this._startDate.toLocaleFormat(otherYearFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
_formatEventTime(event) {
|
||||||
|
const allDay = event.allDay ||
|
||||||
|
(event.date <= this._startDate && event.end >= this._endDate);
|
||||||
|
|
||||||
|
let title;
|
||||||
|
if (allDay) {
|
||||||
|
/* Translators: Shown in calendar event list for all day events
|
||||||
|
* Keep it short, best if you can use less then 10 characters
|
||||||
|
*/
|
||||||
|
title = C_('event list time', 'All Day');
|
||||||
|
} else {
|
||||||
|
let date = event.date >= this._startDate ? event.date : event.end;
|
||||||
|
title = Util.formatTime(date, { timeOnly: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
|
||||||
|
if (event.date < this._startDate && !event.allDay) {
|
||||||
|
if (rtl)
|
||||||
|
title = '%s%s'.format(title, ELLIPSIS_CHAR);
|
||||||
|
else
|
||||||
|
title = '%s%s'.format(ELLIPSIS_CHAR, title);
|
||||||
|
}
|
||||||
|
if (event.end > this._endDate && !event.allDay) {
|
||||||
|
if (rtl)
|
||||||
|
title = '%s%s'.format(ELLIPSIS_CHAR, title);
|
||||||
|
else
|
||||||
|
title = '%s%s'.format(title, ELLIPSIS_CHAR);
|
||||||
|
}
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
_reloadEvents() {
|
||||||
|
if (this._eventSource.isLoading || this._reloading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._reloading = true;
|
||||||
|
|
||||||
|
[...this._eventsList].forEach(c => c.destroy());
|
||||||
|
|
||||||
|
const events =
|
||||||
|
this._eventSource.getEvents(this._startDate, this._endDate);
|
||||||
|
|
||||||
|
for (let event of events) {
|
||||||
|
const box = new St.BoxLayout({
|
||||||
|
style_class: 'event-box',
|
||||||
|
vertical: true,
|
||||||
|
});
|
||||||
|
box.add(new St.Label({
|
||||||
|
text: event.summary,
|
||||||
|
style_class: 'event-summary',
|
||||||
|
}));
|
||||||
|
box.add(new St.Label({
|
||||||
|
text: this._formatEventTime(event),
|
||||||
|
style_class: 'event-time',
|
||||||
|
}));
|
||||||
|
this._eventsList.add_child(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._eventsList.get_n_children() === 0) {
|
||||||
|
const placeholder = new St.Label({
|
||||||
|
text: _('No Events'),
|
||||||
|
style_class: 'event-placeholder',
|
||||||
|
});
|
||||||
|
this._eventsList.add_child(placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._reloading = false;
|
||||||
|
this._sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
vfunc_clicked() {
|
||||||
|
Main.overview.hide();
|
||||||
|
Main.panel.closeCalendar();
|
||||||
|
|
||||||
|
let appInfo = this._calendarApp;
|
||||||
|
if (appInfo.get_id() === 'org.gnome.Evolution.desktop') {
|
||||||
|
const app = this._appSys.lookup_app('evolution-calendar.desktop');
|
||||||
|
if (app)
|
||||||
|
appInfo = app.app_info;
|
||||||
|
}
|
||||||
|
appInfo.launch([], global.create_app_launch_context(0, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
_appInstalledChanged() {
|
||||||
|
const apps = Gio.AppInfo.get_recommended_for_type('text/calendar');
|
||||||
|
if (apps && (apps.length > 0)) {
|
||||||
|
const app = Gio.AppInfo.get_default_for_type('text/calendar', false);
|
||||||
|
const defaultInRecommended = apps.some(a => a.equal(app));
|
||||||
|
this._calendarApp = defaultInRecommended ? app : apps[0];
|
||||||
|
} else {
|
||||||
|
this._calendarApp = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
_sync() {
|
||||||
|
this.visible = this._eventSource && this._eventSource.hasCalendars;
|
||||||
|
this.reactive = this._calendarApp !== null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var WorldClocksSection = GObject.registerClass(
|
var WorldClocksSection = GObject.registerClass(
|
||||||
class WorldClocksSection extends St.Button {
|
class WorldClocksSection extends St.Button {
|
||||||
_init() {
|
_init() {
|
||||||
@ -632,6 +818,7 @@ class DateMenuButton extends PanelMenu.Button {
|
|||||||
this._calendar.connect('selected-date-changed', (_calendar, datetime) => {
|
this._calendar.connect('selected-date-changed', (_calendar, datetime) => {
|
||||||
let date = _gDateTimeToDate(datetime);
|
let date = _gDateTimeToDate(datetime);
|
||||||
layout.frozen = !_isToday(date);
|
layout.frozen = !_isToday(date);
|
||||||
|
this._eventsItem.setDate(date);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.menu.connect('open-state-changed', (menu, isOpen) => {
|
this.menu.connect('open-state-changed', (menu, isOpen) => {
|
||||||
@ -640,6 +827,7 @@ class DateMenuButton extends PanelMenu.Button {
|
|||||||
let now = new Date();
|
let now = new Date();
|
||||||
this._calendar.setDate(now);
|
this._calendar.setDate(now);
|
||||||
this._date.setDate(now);
|
this._date.setDate(now);
|
||||||
|
this._eventsItem.setDate(now);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -670,6 +858,9 @@ class DateMenuButton extends PanelMenu.Button {
|
|||||||
style_class: 'datemenu-displays-box' });
|
style_class: 'datemenu-displays-box' });
|
||||||
this._displaysSection.add_actor(displaysBox);
|
this._displaysSection.add_actor(displaysBox);
|
||||||
|
|
||||||
|
this._eventsItem = new EventsSection();
|
||||||
|
displaysBox.add_child(this._eventsItem);
|
||||||
|
|
||||||
this._clocksItem = new WorldClocksSection();
|
this._clocksItem = new WorldClocksSection();
|
||||||
displaysBox.add_child(this._clocksItem);
|
displaysBox.add_child(this._clocksItem);
|
||||||
|
|
||||||
@ -695,6 +886,7 @@ class DateMenuButton extends PanelMenu.Button {
|
|||||||
this._eventSource.destroy();
|
this._eventSource.destroy();
|
||||||
|
|
||||||
this._calendar.setEventSource(eventSource);
|
this._calendar.setEventSource(eventSource);
|
||||||
|
this._eventsItem.setEventSource(eventSource);
|
||||||
|
|
||||||
this._eventSource = eventSource;
|
this._eventSource = eventSource;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user