[gnome-shell/datetime] Start implementing the Date and Time mockups



commit 85ec4d86f3f8acba5c77b02ac9e4bb9c42d37f5e
Author: David Zeuthen <davidz redhat com>
Date:   Wed Oct 13 17:36:52 2010 -0400

    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            |  104 ++++++++++++++++++------
 js/ui/dateMenu.js            |  189 ++++++++++++++++++++++++++++++++++++++++++
 js/ui/main.js                |    2 +-
 js/ui/panel.js               |   18 +---
 8 files changed, 386 insertions(+), 53 deletions(-)
---
diff --git a/data/clock-preferences.ui b/data/clock-preferences.ui
index 2e22d9a..68e60ce 100644
--- a/data/clock-preferences.ui
+++ b/data/clock-preferences.ui
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<interface domain="gnome-shell">
+<interface>
   <requires lib="gtk+" version="2.16"/>
   <!-- interface-naming-policy project-wide -->
   <object class="GtkDialog" id="prefs-dialog">
@@ -145,6 +145,54 @@
                 <property name="position">1</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkFrame" id="frame3">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment3">
+                    <property name="visible">True</property>
+                    <property name="top_padding">6</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkVBox" id="vbox3">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkCheckButton" id="week_check">
+                            <property name="label" translatable="yes">Show _week numbers</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label_display1">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Calendar</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
           </object>
           <packing>
             <property name="padding">6</property>
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index c14709c..ab73548 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 f35026e..199321c 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 f35d8e2..866fc30 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 078c69d..1ead975 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() });
+            let style_class;
+
+            style_class = 'calendar-day-base calendar-day';
+            if (_isWorkDay(iter))
+                style_class += ' calendar-work-day'
+            else
+                style_class += ' calendar-nonwork-day'
+
             if (_sameDay(now, iter))
-                label.style_class = 'calendar-day calendar-today';
+                style_class += ' calendar-today';
             else if (iter.getMonth() != this.date.getMonth())
-                label.style_class = 'calendar-day calendar-other-month-day';
-            else
-                label.style_class = 'calendar-day';
+                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 0000000..77d7bba
--- /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 a0287c3..cdab69c 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 26c3a03..4abbcf2 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,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]