From 85ec4d86f3f8acba5c77b02ac9e4bb9c42d37f5e Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Wed, 13 Oct 2010 17:36:52 -0400 Subject: [PATCH] Start implementing the Date and Time mockups --- data/clock-preferences.ui | 50 ++++++++- data/theme/gnome-shell.css | 62 +++++++++--- js/Makefile.am | 1 + js/prefs/clockPreferences.js | 13 +++ js/ui/calendar.js | 106 +++++++++++++++----- js/ui/dateMenu.js | 189 +++++++++++++++++++++++++++++++++++ js/ui/main.js | 2 +- js/ui/panel.js | 18 +--- 8 files changed, 387 insertions(+), 54 deletions(-) create mode 100644 js/ui/dateMenu.js diff --git a/data/clock-preferences.ui b/data/clock-preferences.ui index 2e22d9aa9..68e60cedc 100644 --- a/data/clock-preferences.ui +++ b/data/clock-preferences.ui @@ -1,5 +1,5 @@ - + @@ -145,6 +145,54 @@ 1 + + + True + 0 + none + + + True + 6 + 12 + + + True + vertical + 6 + + + Show _week numbers + True + True + False + True + True + + + 0 + + + + + + + + + True + Calendar + + + + + + + + False + False + 2 + + 6 diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index c14709c6a..ab735482f 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -795,6 +795,14 @@ StTooltip { /* Calendar popup */ +.calendar-vertical-separator { + -gradient-width: 2px; + -gradient-start: rgba(8,8,8,0); + -gradient-end: #333333; + -margin-vertical: 1.5em; + width: 1em; +} + #calendarPopup { border-radius: 5px; background: rgba(0,0,0,0.9); @@ -807,37 +815,69 @@ StTooltip { } .calendar { - spacing-rows: 5px; - spacing-columns: 3px; + spacing-rows: 0px; + spacing-columns: 0px; } .calendar-change-month { + color: #666666; + font-size: 10px; padding: 2px; } .calendar-change-month:hover { - background: #314a6c; + background: #999999; border-radius: 5px; } .calendar-change-month:active { - background: #213050; + background: #aaaaaa; border-radius: 5px; } +.datemenu-date-label { + font-size: 16px; + font-weight: bold; + color: #999999; +} + +.calendar-day-base { + font-size: 10px; + padding: 5px 3px; + text-align: center; +} + +.calendar-day-heading { + color: #666666; +} + +.calendar-week-number { + color: #666666; + font-weight: bold; +} + .calendar-day { - padding: 1px 2px; + border: 1px solid #333333; + color: #cccccc; +} + +.calendar-work-day { +} + +.calendar-nonwork-day { + background: #181818; } .calendar-today { + color: #ffffff; font-weight: bold; - background: #ffffff; - color: black; - border-radius: 5px; + background-gradient-direction: vertical; + background-gradient-start: #3c3c3c; + background-gradient-end: #131313; } .calendar-other-month-day { - color: #cccccc; + color: #333333; } /* Message Tray */ @@ -988,10 +1028,6 @@ StTooltip { padding-left: 4px; } -.calendar-calendarweek { - color: #666666; -} - /* App Switcher */ #altTabPopup { padding: 8px; diff --git a/js/Makefile.am b/js/Makefile.am index f35026e81..199321ce0 100644 --- a/js/Makefile.am +++ b/js/Makefile.am @@ -17,6 +17,7 @@ nobase_dist_js_DATA = \ ui/calendar.js \ ui/chrome.js \ ui/dash.js \ + ui/dateMenu.js \ ui/dnd.js \ ui/docDisplay.js \ ui/environment.js \ diff --git a/js/prefs/clockPreferences.js b/js/prefs/clockPreferences.js index f35d8e250..866fc3032 100644 --- a/js/prefs/clockPreferences.js +++ b/js/prefs/clockPreferences.js @@ -13,6 +13,7 @@ const FORMAT_KEY = 'format'; const SHOW_DATE_KEY = 'show-date'; const SHOW_SECONDS_KEY = 'show-seconds'; +const SHOW_WEEKDATE_KEY = 'show-weekdate'; function ClockPreferences(uiFile) { this._init(uiFile); @@ -30,6 +31,7 @@ ClockPreferences.prototype = { this._24hrRadio = builder.get_object('24hr_radio'); this._dateCheck = builder.get_object('date_check'); this._secondsCheck = builder.get_object('seconds_check'); + this._weekCheck = builder.get_object('week_check'); delete builder; @@ -37,6 +39,10 @@ ClockPreferences.prototype = { this._notifyId = this._settings.connect('changed', Lang.bind(this, this._updateDialog)); + this._calendar_settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' }); + this._calendar_notifyId = this._calendar_settings.connect('changed', + Lang.bind(this, + this._updateDialog)); this._12hrRadio.connect('toggled', Lang.bind(this, function() { @@ -53,6 +59,11 @@ ClockPreferences.prototype = { this._settings.set_boolean(SHOW_SECONDS_KEY, this._secondsCheck.active); })); + this._weekCheck.connect('toggled', Lang.bind(this, + function() { + this._calendar_settings.set_boolean(SHOW_WEEKDATE_KEY, + this._weekCheck.active); + })); this._updateDialog(); }, @@ -68,11 +79,13 @@ ClockPreferences.prototype = { this._dateCheck.active = this._settings.get_boolean(SHOW_DATE_KEY); this._secondsCheck.active = this._settings.get_boolean(SHOW_SECONDS_KEY); + this._weekCheck.active = this._calendar_settings.get_boolean(SHOW_WEEKDATE_KEY); }, _onResponse: function() { this._dialog.destroy(); this._settings.disconnect(this._notifyId); + this._calendar_settings.disconnect(this._calendar_notifyId); this.emit('destroy'); } }; diff --git a/js/ui/calendar.js b/js/ui/calendar.js index 078c69d82..1ead975ef 100644 --- a/js/ui/calendar.js +++ b/js/ui/calendar.js @@ -6,6 +6,8 @@ const Lang = imports.lang; const St = imports.gi.St; const Pango = imports.gi.Pango; const Gettext_gtk20 = imports.gettext.domain('gtk20'); +const Gettext = imports.gettext.domain('gnome-shell'); +const _ = Gettext.gettext; const MSECS_IN_DAY = 24 * 60 * 60 * 1000; const WEEKDATE_HEADER_WIDTH_DIGITS = 3; @@ -17,6 +19,14 @@ function _sameDay(dateA, dateB) { dateA.getYear() == dateB.getYear()); } +/* TODO: maybe needs config - right now we assume that Saturday and + * Sunday are work days (not true in e.g. Israel, it's Sunday and + * Monday there) + */ +function _isWorkDay(date) { + return date.getDay() != 0 && date.getDay() != 6; +} + function _getCalendarWeekForDate(date) { // Based on the algorithms found here: // http://en.wikipedia.org/wiki/Talk:ISO_week_date @@ -43,6 +53,50 @@ function _getDigitWidth(actor){ return width; } +function _getCustomDayAbrreviation(day_number) { + let ret; + switch (day_number) { + case 0: + /* Translators: One-letter abbreaviation for Sunday - note: + * all one-letter abbreviations are always shown together and + * in order, e.g. "S M T W T F S" + */ + ret = _("S"); + break; + + case 1: + /* Translators: One-letter abbreaviation for Monday */ + ret = _("M"); + break; + + case 2: + /* Translators: One-letter abbreaviation for Tuesday */ + ret = _("T"); + break; + + case 3: + /* Translators: One-letter abbreaviation for Wednesday */ + ret = _("W"); + break; + + case 4: + /* Translators: One-letter abbreaviation for Thursday */ + ret = _("T"); + break; + + case 5: + /* Translators: One-letter abbreaviation for Friday */ + ret = _("F"); + break; + + case 6: + /* Translators: One-letter abbreaviation for Saturday */ + ret = _("S"); + break; + } + return ret; +} + function Calendar() { this._init(); } @@ -125,36 +179,32 @@ Calendar.prototype = { this._topBox.add(back); back.connect('clicked', Lang.bind(this, this._prevMonth)); - this._dateLabel = new St.Label(); + this._dateLabel = new St.Label({style_class: 'calendar-change-month'}); this._topBox.add(this._dateLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE }); let forward = new St.Button({ label: forwardlabel, style_class: 'calendar-change-month' }); this._topBox.add(forward); forward.connect('clicked', Lang.bind(this, this._nextMonth)); + // Add weekday labels... + // // We need to figure out the abbreviated localized names for the days of the week; // we do this by just getting the next 7 days starting from right now and then putting // them in the right cell in the table. It doesn't matter if we add them in order + // let iter = new Date(this.date); iter.setSeconds(0); // Leap second protection. Hah! iter.setHours(12); - - if (this._useWeekdate) { - this._weekdateHeader = new St.Label(); - this.actor.add(this._weekdateHeader, - { row: 1, - col: 0, - x_fill: false, x_align: St.Align.MIDDLE }); - this._setWeekdateHeaderWidth(); - } else { - this._weekdateHeader = null; - } - for (let i = 0; i < 7; i++) { - this.actor.add(new St.Label({ text: iter.toLocaleFormat('%a') }), + // Could use iter.toLocaleFormat('%a') but that normally gives three characters + // and we want, ideally, a single character for e.g. S M T W T F S + let custom_day_abbrev = _getCustomDayAbrreviation(iter.getDay()); + let label = new St.Label({ text: custom_day_abbrev }); + label.style_class = 'calendar-day-base calendar-day-heading'; + this.actor.add(label, { row: 1, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7, - x_fill: false, x_align: St.Align.END }); + x_fill: false, x_align: St.Align.MIDDLE }); iter.setTime(iter.getTime() + MSECS_IN_DAY); } @@ -234,24 +284,30 @@ Calendar.prototype = { let row = 2; while (true) { let label = new St.Label({ text: iter.getDate().toString() }); - if (_sameDay(now, iter)) - label.style_class = 'calendar-day calendar-today'; - else if (iter.getMonth() != this.date.getMonth()) - label.style_class = 'calendar-day calendar-other-month-day'; + let style_class; + + style_class = 'calendar-day-base calendar-day'; + if (_isWorkDay(iter)) + style_class += ' calendar-work-day' else - label.style_class = 'calendar-day'; + style_class += ' calendar-nonwork-day' + + if (_sameDay(now, iter)) + style_class += ' calendar-today'; + else if (iter.getMonth() != this.date.getMonth()) + style_class += ' calendar-other-month-day'; + + label.style_class = style_class; let offsetCols = this._useWeekdate ? 1 : 0; this.actor.add(label, - { row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7, - x_fill: false, x_align: St.Align.END }); + { row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 }); if (this._useWeekdate && iter.getDay() == 4) { let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(), - style_class: 'calendar-day calendar-calendarweek'}); + style_class: 'calendar-day-base calendar-week-number'}); this.actor.add(label, - { row: row, col: 0, - x_fill: false, x_align: St.Align.MIDDLE }); + { row: row, col: 0, y_align: St.Align.MIDDLE }); } iter.setTime(iter.getTime() + MSECS_IN_DAY); diff --git a/js/ui/dateMenu.js b/js/ui/dateMenu.js new file mode 100644 index 000000000..77d7bba30 --- /dev/null +++ b/js/ui/dateMenu.js @@ -0,0 +1,189 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ + +const GLib = imports.gi.GLib; +const Gio = imports.gi.Gio; +const Lang = imports.lang; +const Mainloop = imports.mainloop; +const Cairo = imports.cairo; +const Clutter = imports.gi.Clutter; +const Shell = imports.gi.Shell; +const St = imports.gi.St; +const Gettext = imports.gettext.domain('gnome-shell'); +const _ = Gettext.gettext; + +const Main = imports.ui.main; +const PanelMenu = imports.ui.panelMenu; +const PopupMenu = imports.ui.popupMenu; +const Calendar = imports.ui.calendar; + +const CLOCK_FORMAT_KEY = 'format'; +const CLOCK_CUSTOM_FORMAT_KEY = 'custom-format'; +const CLOCK_SHOW_DATE_KEY = 'show-date'; +const CLOCK_SHOW_SECONDS_KEY = 'show-seconds'; + +function DateMenuButton() { + this._init(); +} + +function on_vert_sep_repaint (area) +{ + let cr = area.get_context(); + let themeNode = area.get_theme_node(); + let [width, height] = area.get_surface_size(); + let found, margin, gradientHeight; + [found, margin] = themeNode.get_length('-margin-vertical', false); + [found, gradientWidth] = themeNode.get_length('-gradient-width', false); + let startColor = new Clutter.Color(); + themeNode.get_color('-gradient-start', false, startColor); + let endColor = new Clutter.Color(); + themeNode.get_color('-gradient-end', false, endColor); + + let gradientHeight = (height - margin * 2); + let gradientOffset = (width - gradientWidth) / 2; + let pattern = new Cairo.LinearGradient(gradientOffset, margin, gradientOffset + gradientWidth, height - margin); + pattern.addColorStopRGBA(0, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255); + pattern.addColorStopRGBA(0.5, endColor.red / 255, endColor.green / 255, endColor.blue / 255, endColor.alpha / 255); + pattern.addColorStopRGBA(1, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255); + cr.setSource(pattern); + cr.rectangle(gradientOffset, margin, gradientWidth, gradientHeight); + cr.fill(); +}; + +DateMenuButton.prototype = { + __proto__: PanelMenu.Button.prototype, + + _init: function() { + let item; + + PanelMenu.Button.prototype._init.call(this, St.Align.START); + + this._clock = new St.Label(); + this.actor.set_child(this._clock); + + this._date = new St.Label(); + this._date.style_class = 'datemenu-date-label'; + this.menu._box.add(this._date); + + this._calendar = new Calendar.Calendar(); + this.menu._box.add(this._calendar.actor); + + item = new PopupMenu.PopupSeparatorMenuItem(); + this.menu.addMenuItem(item); + + item = new PopupMenu.PopupImageMenuItem(_("Date and Time Settings"), 'gnome-shell-clock-preferences'); + item.connect('activate', Lang.bind(this, this._onPreferencesActivate)); + this.menu.addMenuItem(item); + + this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' }); + this._clockSettings.connect('changed', Lang.bind(this, this._clockSettingsChanged)); + + this._vertSep = new St.DrawingArea({ style_class: 'calendar-vertical-separator', + pseudo_class: 'highlighted' }); + //this._vertSep.set_width (25); + this._vertSep.connect('repaint', Lang.bind(this, on_vert_sep_repaint)); + + let hbox; + let orig_menu_box; + orig_menu_box = this.menu._box; + this.menu._boxPointer.bin.remove_actor(orig_menu_box); + hbox = new St.BoxLayout(); + hbox.add(orig_menu_box); + hbox.add(this._vertSep); + hbox.add(new St.Label({text: "foo0"})); + this.menu._boxPointer.bin.set_child(hbox); + this.menu._box = hbox; + + // Start the clock + this._updateClockAndDate(); + }, + + _clockSettingsChanged: function() { + this._updateClockAndDate(); + }, + + _updateClockAndDate: function() { + let format = this._clockSettings.get_string(CLOCK_FORMAT_KEY); + let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY); + let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY); + + let clockFormat; + let dateFormat; + + switch (format) { + case 'unix': + // force updates every second + showSeconds = true; + clockFormat = '%s'; + break; + case 'custom': + // force updates every second + showSeconds = true; + clockFormat = this._clockSettings.get_string(CLOCK_CUSTOM_FORMAT_KEY); + break; + case '24-hour': + if (showDate) + /* Translators: This is the time format with date used + in 24-hour mode. */ + clockFormat = showSeconds ? _("%a %b %e, %R:%S") + : _("%a %b %e, %R"); + else + /* Translators: This is the time format without date used + in 24-hour mode. */ + clockFormat = showSeconds ? _("%a %R:%S") + : _("%a %R"); + break; + case '12-hour': + default: + if (showDate) + /* Translators: This is a time format with date used + for AM/PM. */ + clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p") + : _("%a %b %e, %l:%M %p"); + else + /* Translators: This is a time format without date used + for AM/PM. */ + clockFormat = showSeconds ? _("%a %l:%M:%S %p") + : _("%a %l:%M %p"); + break; + } + + let displayDate = new Date(); + let msecRemaining; + if (showSeconds) { + msecRemaining = 1000 - displayDate.getMilliseconds(); + if (msecRemaining < 50) { + displayDate.setSeconds(displayDate.getSeconds() + 1); + msecRemaining += 1000; + } + } else { + msecRemaining = 60000 - (1000 * displayDate.getSeconds() + + displayDate.getMilliseconds()); + if (msecRemaining < 500) { + displayDate.setMinutes(displayDate.getMinutes() + 1); + msecRemaining += 60000; + } + } + + this._clock.set_text(displayDate.toLocaleFormat(clockFormat)); + + /* Translators: This is the date format to use */ + dateFormat = _("%B %e, %Y"); + this._date.set_text(displayDate.toLocaleFormat(dateFormat)); + + Mainloop.timeout_add(msecRemaining, Lang.bind(this, this._updateClockAndDate)); + return false; + }, + + _onPreferencesActivate: function() { + Main.overview.hide(); + this._spawn(['gnome-shell-clock-preferences']); + }, + + _spawn: function(args) { + // FIXME: once Shell.Process gets support for signalling + // errors we should pop up an error dialog or something here + // on failure + let p = new Shell.Process({'args' : args}); + p.run(); + } +}; diff --git a/js/ui/main.js b/js/ui/main.js index a0287c38e..cdab69c5f 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -247,7 +247,7 @@ function _relayout() { // will be updated when it is next shown. We do the same for // the calendar popdown. overview.hide(); - panel.hideCalendar(); + //panel.hideCalendar(); } // metacity-clutter currently uses the same prefs as plain metacity, diff --git a/js/ui/panel.js b/js/ui/panel.js index 26c3a0330..4abbcf26c 100644 --- a/js/ui/panel.js +++ b/js/ui/panel.js @@ -16,6 +16,7 @@ const Overview = imports.ui.overview; const PopupMenu = imports.ui.popupMenu; const PanelMenu = imports.ui.panelMenu; const StatusMenu = imports.ui.statusMenu; +const DateMenu = imports.ui.dateMenu; const Main = imports.ui.main; const Tweener = imports.ui.tweener; @@ -34,11 +35,6 @@ const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = { 'a11y': imports.ui.status.accessibility.ATIndicator }; -const CLOCK_FORMAT_KEY = 'format'; -const CLOCK_CUSTOM_FORMAT_KEY = 'custom-format'; -const CLOCK_SHOW_DATE_KEY = 'show-date'; -const CLOCK_SHOW_SECONDS_KEY = 'show-seconds'; - function AnimatedIcon(name, size) { this._init(name, size); } @@ -792,11 +788,9 @@ Panel.prototype = { this._menus.addMenu(appMenuButton.menu); /* center */ - - this._clockButton = new ClockButton(); - this._centerBox.add(this._clockButton.actor, { y_fill: true }); - - this._menus.addMenu(this._clockButton.menu); + this._dateMenu = new DateMenu.DateMenuButton(); + this._centerBox.add(this._dateMenu.actor, { y_fill: true }); + this._menus.addMenu(this._dateMenu.menu); /* right */ @@ -856,10 +850,6 @@ Panel.prototype = { Main.chrome.addActor(this.actor, { visibleInOverview: true }); }, - hideCalendar: function() { - this._clockButton.closeCalendar(); - }, - startupAnimation: function() { this.actor.y = -this.actor.height; Tweener.addTween(this.actor,