Author: julien
Date: 2011-08-31 01:07:07 -0700 (Wed, 31 Aug 2011)
New Revision: 16714

Added:
   django/trunk/django/contrib/admin/static/admin/img/selector-icons.gif
Removed:
   django/trunk/django/contrib/admin/static/admin/img/selector-add.gif
   django/trunk/django/contrib/admin/static/admin/img/selector-addall.gif
   django/trunk/django/contrib/admin/static/admin/img/selector-remove.gif
   django/trunk/django/contrib/admin/static/admin/img/selector-removeall.gif
   django/trunk/django/contrib/admin/static/admin/img/selector_stacked-add.gif
   
django/trunk/django/contrib/admin/static/admin/img/selector_stacked-remove.gif
Modified:
   django/trunk/django/contrib/admin/static/admin/css/widgets.css
   django/trunk/django/contrib/admin/static/admin/js/SelectFilter2.js
   django/trunk/docs/releases/1.4.txt
Log:
Fixed #16059 -- Improved the usability of the admin's vertical and horizontal 
"filter" widgets, in particular by providing a better visual representation of 
the buttons' enabled and disabled states, and by providing more elaborate, yet 
less cluttered, help texts.

Note that this commit is an exception to the current tacit rule that javascript 
code changes should be avoided until a proper javascript testing framework for 
Django core is in place. This exception is commanded by the fact that it is to 
fix a recognized usability issue and that the patch has been (manually) 
extensively tested in IE6+, Chrome, Safari, Firefox and Opera.

Modified: django/trunk/django/contrib/admin/static/admin/css/widgets.css
===================================================================
--- django/trunk/django/contrib/admin/static/admin/css/widgets.css      
2011-08-30 12:11:09 UTC (rev 16713)
+++ django/trunk/django/contrib/admin/static/admin/css/widgets.css      
2011-08-31 08:07:07 UTC (rev 16714)
@@ -17,6 +17,10 @@
     margin-bottom: 5px;
 }
 
+.selector-chosen select {
+    border-top: none;
+}
+
 .selector-available h2, .selector-chosen h2 {
     border: 1px solid #ccc;
 }
@@ -37,8 +41,9 @@
     text-align: left;
 }
 
-.selector .selector-chosen .selector-filter {
-    padding: 4px 5px;
+.selector .selector-filter label {
+    width: 16px;
+    padding: 2px;
 }
 
 .selector .selector-available input {
@@ -50,7 +55,7 @@
     width: 22px;
     height: 50px;
     background: url(../img/chooser-bg.gif) top center no-repeat;
-    margin: 8em 3px 0 3px;
+    margin: 10em 5px 0 5px;
     padding: 0;
 }
 
@@ -61,7 +66,7 @@
 }
 
 .selector select {
-    margin-bottom: 5px;
+    margin-bottom: 10px;
     margin-top: 0;
 }
 
@@ -74,38 +79,66 @@
 }
 
 .selector-add {
-    background: url(../img/selector-add.gif) top center no-repeat;
+    background: url(../img/selector-icons.gif) 0 -161px no-repeat;
+    cursor: default;
     margin-bottom: 2px;
 }
 
+.active.selector-add {
+    background: url(../img/selector-icons.gif) 0 -187px no-repeat;
+    cursor: pointer;
+}
+
 .selector-remove {
-    background: url(../img/selector-remove.gif) top center no-repeat;
+    background: url(../img/selector-icons.gif) 0 -109px no-repeat;
+    cursor: default;
 }
 
+.active.selector-remove {
+    background: url(../img/selector-icons.gif) 0 -135px no-repeat;
+    cursor: pointer;
+}
+
 a.selector-chooseall, a.selector-clearall {
-    display: block;
-    width: 6em;
+    display: inline-block;
     text-align: left;
     margin-left: auto;
     margin-right: auto;
     font-weight: bold;
     color: #666;
+}
+
+a.selector-chooseall {
+    padding: 3px 18px 3px 0;
+}
+
+a.selector-clearall {
     padding: 3px 0 3px 18px;
 }
 
-a.selector-chooseall:hover, a.selector-clearall:hover {
+a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
     color: #036;
 }
 
 a.selector-chooseall {
-    width: 7em;
-    background: url(../img/selector-addall.gif) left center no-repeat;
+    background: url(../img/selector-icons.gif) right -263px no-repeat;
+    cursor: default;
 }
 
+a.active.selector-chooseall {
+    background: url(../img/selector-icons.gif) right -289px no-repeat;
+    cursor: pointer;
+}
+
 a.selector-clearall {
-    background: url(../img/selector-removeall.gif) left center no-repeat;
+    background: url(../img/selector-icons.gif) left -211px no-repeat;
+    cursor: default;
 }
 
+a.active.selector-clearall {
+    background: url(../img/selector-icons.gif) left -237px no-repeat;
+    cursor: pointer;
+}
 
 /* STACKED SELECTORS */
 
@@ -148,13 +181,24 @@
 }
 
 .stacked .selector-add {
-    background-image: url(../img/selector_stacked-add.gif);
+    background: url(../img/selector-icons.gif) 0 -57px no-repeat;
+    cursor: default;
 }
 
+.stacked .active.selector-add {
+    background: url(../img/selector-icons.gif) 0 -83px no-repeat;
+    cursor: pointer;
+}
+
 .stacked .selector-remove {
-    background-image: url(../img/selector_stacked-remove.gif);
+    background: url(../img/selector-icons.gif) 0 -5px no-repeat;
+    cursor: default;
 }
 
+.stacked .active.selector-remove {
+    background: url(../img/selector-icons.gif) 0 -31px no-repeat;
+    cursor: pointer;
+}
 
 /* DATE AND TIME */
 

Deleted: django/trunk/django/contrib/admin/static/admin/img/selector-add.gif
===================================================================
(Binary files differ)

Deleted: django/trunk/django/contrib/admin/static/admin/img/selector-addall.gif
===================================================================
(Binary files differ)

Added: django/trunk/django/contrib/admin/static/admin/img/selector-icons.gif
===================================================================
(Binary files differ)


Property changes on: 
django/trunk/django/contrib/admin/static/admin/img/selector-icons.gif
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Deleted: django/trunk/django/contrib/admin/static/admin/img/selector-remove.gif
===================================================================
(Binary files differ)

Deleted: 
django/trunk/django/contrib/admin/static/admin/img/selector-removeall.gif
===================================================================
(Binary files differ)

Deleted: 
django/trunk/django/contrib/admin/static/admin/img/selector_stacked-add.gif
===================================================================
(Binary files differ)

Deleted: 
django/trunk/django/contrib/admin/static/admin/img/selector_stacked-remove.gif
===================================================================
(Binary files differ)

Modified: django/trunk/django/contrib/admin/static/admin/js/SelectFilter2.js
===================================================================
--- django/trunk/django/contrib/admin/static/admin/js/SelectFilter2.js  
2011-08-30 12:11:09 UTC (rev 16713)
+++ django/trunk/django/contrib/admin/static/admin/js/SelectFilter2.js  
2011-08-31 08:07:07 UTC (rev 16714)
@@ -5,7 +5,7 @@
 
 Requires core.js, SelectBox.js and addevent.js.
 */
-
+(function($) {
 function findForm(node) {
     // returns the node of the form containing the given node
     if (node.tagName.toLowerCase() != 'form') {
@@ -14,7 +14,7 @@
     return node;
 }
 
-var SelectFilter = {
+window.SelectFilter = {
     init: function(field_id, field_name, is_stacked, admin_media_prefix) {
         if (field_id.match(/__prefix__/)){
             // Don't intialize on empty forms.
@@ -44,41 +44,42 @@
         // <div class="selector-available">
         var selector_available = quickElement('div', selector_div, '');
         selector_available.className = 'selector-available';
-        quickElement('h2', selector_available, interpolate(gettext('Available 
%s'), [field_name]));
-        var filter_p = quickElement('p', selector_available, '');
+        var title_available = quickElement('h2', selector_available, 
interpolate(gettext('Available %s') + ' ', [field_name]));
+        quickElement('img', title_available, '', 'src', admin_media_prefix + 
'img/icon-unknown.gif', 'width', '10', 'height', '10', 'class', 'help 
help-tooltip', 'title', interpolate(gettext('This is the list of available %s. 
You may add some by selecting them below and then clicking the "Add" button.'), 
[field_name]));
+
+        var filter_p = quickElement('p', selector_available, '', 'id', 
field_id + '_filter');
         filter_p.className = 'selector-filter';
 
-        var search_filter_label = quickElement('label', filter_p, '', 'for', 
field_id + "_input", 'style', 'width:16px;padding:2px');
+        var search_filter_label = quickElement('label', filter_p, '', 'for', 
field_id + "_input");
 
-        var search_selector_img = quickElement('img', search_filter_label, '', 
'src', admin_media_prefix + 'img/selector-search.gif');
-        search_selector_img.alt = gettext("Filter");
+        var search_selector_img = quickElement('img', search_filter_label, '', 
'src', admin_media_prefix + 'img/selector-search.gif', 'class', 'help-tooltip', 
'alt', '', 'title', interpolate(gettext("Type into the filter box to narrow 
down the list of available %s."), [field_name]));
 
         filter_p.appendChild(document.createTextNode(' '));
 
-        var filter_input = quickElement('input', filter_p, '', 'type', 'text');
+        var filter_input = quickElement('input', filter_p, '', 'type', 'text', 
'placeholder', gettext("Filter"));
         filter_input.id = field_id + '_input';
+
         selector_available.appendChild(from_box);
-        var choose_all = quickElement('a', selector_available, gettext('Choose 
all'), 'href', 'javascript: (function(){ SelectBox.move_all("' + field_id + 
'_from", "' + field_id + '_to"); })()');
+        var choose_all = quickElement('a', selector_available, gettext('Add 
all'), 'href', 'javascript: (function(){ SelectBox.move_all("' + field_id + 
'_from", "' + field_id + '_to"); SelectFilter.refresh_icons("' + field_id + 
'");})()', 'id', field_id + '_add_all_link');
         choose_all.className = 'selector-chooseall';
 
         // <ul class="selector-chooser">
         var selector_chooser = quickElement('ul', selector_div, '');
         selector_chooser.className = 'selector-chooser';
-        var add_link = quickElement('a', quickElement('li', selector_chooser, 
''), gettext('Add'), 'href', 'javascript: (function(){ SelectBox.move("' + 
field_id + '_from","' + field_id + '_to");})()');
+        var add_link = quickElement('a', quickElement('li', selector_chooser, 
''), gettext('Add'), 'title', gettext('Add'), 'href', 'javascript: (function(){ 
SelectBox.move("' + field_id + '_from","' + field_id + '_to"); 
SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + 
'_add_link');
         add_link.className = 'selector-add';
-        var remove_link = quickElement('a', quickElement('li', 
selector_chooser, ''), gettext('Remove'), 'href', 'javascript: (function(){ 
SelectBox.move("' + field_id + '_to","' + field_id + '_from");})()');
+        var remove_link = quickElement('a', quickElement('li', 
selector_chooser, ''), gettext('Remove'), 'title', gettext('Remove'), 'href', 
'javascript: (function(){ SelectBox.move("' + field_id + '_to","' + field_id + 
'_from"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id 
+ '_remove_link');
         remove_link.className = 'selector-remove';
 
         // <div class="selector-chosen">
         var selector_chosen = quickElement('div', selector_div, '');
         selector_chosen.className = 'selector-chosen';
-        quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s'), 
[field_name]));
-        var selector_filter = quickElement('p', selector_chosen, 
gettext('Select your choice(s) and click '));
-        selector_filter.className = 'selector-filter';
-        quickElement('img', selector_filter, '', 'src', admin_media_prefix + 
(is_stacked ? 'img/selector_stacked-add.gif':'img/selector-add.gif'), 'alt', 
'Add');
+        var title_chosen = quickElement('h2', selector_chosen, 
interpolate(gettext('Added %s') + ' ', [field_name]));
+        quickElement('img', title_chosen, '', 'src', admin_media_prefix + 
'img/icon-unknown.gif', 'width', '10', 'height', '10', 'class', 'help 
help-tooltip', 'title', interpolate(gettext('This is the list of added %s. You 
may remove some by selecting them below and then clicking the "Remove" 
button.'), [field_name]));
+
         var to_box = quickElement('select', selector_chosen, '', 'id', 
field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', 
from_box.getAttribute('name'));
         to_box.className = 'filtered';
-        var clear_all = quickElement('a', selector_chosen, gettext('Clear 
all'), 'href', 'javascript: (function() { SelectBox.move_all("' + field_id + 
'_to", "' + field_id + '_from");})()');
+        var clear_all = quickElement('a', selector_chosen, gettext('Remove 
all'), 'href', 'javascript: (function() { SelectBox.move_all("' + field_id + 
'_to", "' + field_id + '_from"); SelectFilter.refresh_icons("' + field_id + 
'");})()', 'id', field_id + '_remove_all_link');
         clear_all.className = 'selector-clearall';
 
         from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
@@ -86,16 +87,38 @@
         // Set up the JavaScript event handlers for the select box filter 
interface
         addEvent(filter_input, 'keyup', function(e) { 
SelectFilter.filter_key_up(e, field_id); });
         addEvent(filter_input, 'keydown', function(e) { 
SelectFilter.filter_key_down(e, field_id); });
-        addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + 
'_from', field_id + '_to'); });
-        addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + 
'_to', field_id + '_from'); });
+        addEvent(from_box, 'change', function(e) { 
SelectFilter.refresh_icons(field_id) });
+        addEvent(to_box, 'change', function(e) { 
SelectFilter.refresh_icons(field_id) });
+        addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + 
'_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); });
+        addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + 
'_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); });
         addEvent(findForm(from_box), 'submit', function() { 
SelectBox.select_all(field_id + '_to'); });
         SelectBox.init(field_id + '_from');
         SelectBox.init(field_id + '_to');
         // Move selected from_box options to to_box
         SelectBox.move(field_id + '_from', field_id + '_to');
+
+        if (!is_stacked) {
+            // In horizontal mode, give the same height to the two boxes.
+            $(to_box).height($(filter_p).outerHeight() + 
$(from_box).outerHeight());
+        }
+
+        // Initial icon refresh
+        SelectFilter.refresh_icons(field_id);
     },
+    refresh_icons: function(field_id) {
+        var from = $('#' + field_id + '_from');
+        var to = $('#' + field_id + '_to');
+        var is_from_selected = from.find('option:selected').length > 0;
+        var is_to_selected = to.find('option:selected').length > 0;
+        // Active if at least one item is selected
+        $('#' + field_id + '_add_link').toggleClass('active', 
is_from_selected);
+        $('#' + field_id + '_remove_link').toggleClass('active', 
is_to_selected);
+        // Active if the corresponding box isn't empty
+        $('#' + field_id + '_add_all_link').toggleClass('active', 
from.find('option').length > 0);
+        $('#' + field_id + '_remove_all_link').toggleClass('active', 
to.find('option').length > 0);
+    },
     filter_key_up: function(event, field_id) {
-        from = document.getElementById(field_id + '_from');
+        var from = document.getElementById(field_id + '_from');
         // don't submit form if user pressed Enter
         if ((event.which && event.which == 13) || (event.keyCode && 
event.keyCode == 13)) {
             from.selectedIndex = 0;
@@ -109,7 +132,7 @@
         return true;
     },
     filter_key_down: function(event, field_id) {
-        from = document.getElementById(field_id + '_from');
+        var from = document.getElementById(field_id + '_from');
         // right arrow -- move across
         if ((event.which && event.which == 39) || (event.keyCode && 
event.keyCode == 39)) {
             var old_index = from.selectedIndex;
@@ -128,3 +151,5 @@
         return true;
     }
 }
+
+})(django.jQuery);

Modified: django/trunk/docs/releases/1.4.txt
===================================================================
--- django/trunk/docs/releases/1.4.txt  2011-08-30 12:11:09 UTC (rev 16713)
+++ django/trunk/docs/releases/1.4.txt  2011-08-31 08:07:07 UTC (rev 16714)
@@ -328,6 +328,18 @@
     that path. The files were moved from :file:`django/contrib/admin/media/`
     to :file:`django/contrib/admin/static/admin/`.
 
+Removed admin icons
+~~~~~~~~~~~~~~~~~~~
+
+As part of an effort to improve the performance and usability of the admin's
+vertical and horizontal "filter" widgets, some icon files were removed and
+grouped into a single sprite file (``selector-icons.gif``):
+``selector-add.gif``, ``selector-addall.gif``, ``selector-remove.gif``,
+``selector-removeall.gif``, ``selector_stacked-add.gif`` and
+``selector_stacked-remove.gif``. If you used those icons to customize the
+admin then you will want to replace them with your own icons or retrieve them
+from a previous release.
+
 Compatibility with old signed data
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to django-updates@googlegroups.com.
To unsubscribe from this group, send email to 
django-updates+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to