diff -r d12c21ea4cd4 -r ad0581296e2c web/facet.py --- a/web/facet.py Thu Oct 13 09:15:16 2011 +0200 +++ b/web/facet.py Thu Oct 13 09:25:26 2011 +0200 @@ -50,7 +50,7 @@ from warnings import warn from copy import deepcopy -from datetime import date, datetime, timedelta +from datetime import datetime, timedelta from logilab.mtconverter import xml_escape from logilab.common.graph import has_path @@ -59,7 +59,7 @@ from logilab.common.compat import all from logilab.common.deprecation import deprecated -from rql import parse, nodes, utils +from rql import nodes, utils from cubicweb import Unauthorized, typed_eid from cubicweb.schema import display_name @@ -469,6 +469,10 @@ def wdgclass(self): return FacetVocabularyWidget + def get_selected(self): + return frozenset(typed_eid(eid) + for eid in self._cw.list_form_param(self.__regid__)) + def get_widget(self): """Return the widget instance to use to display this facet. @@ -479,12 +483,9 @@ if len(vocab) <= 1: return None wdg = self.wdgclass(self) - selected = frozenset(typed_eid(eid) for eid in self._cw.list_form_param(self.__regid__)) + selected = self.get_selected() for label, value in vocab: - if value is None: - wdg.append(FacetSeparator(label)) - else: - wdg.append(FacetItem(self._cw, label, value, value in selected)) + wdg.items.append((value, label, value in selected)) return wdg def vocabulary(self): @@ -499,7 +500,6 @@ raise NotImplementedError - class RelationFacet(VocabularyFacet): """Base facet to filter some entities according to other entities to which they are related. Create concrete facet by inheriting from this class an then @@ -711,6 +711,7 @@ # internal utilities ####################################################### + @cached def _support_and_compat(self): support = self.support_and if callable(support): @@ -1339,24 +1340,7 @@ ## html widets ################################################################ -_DEFAULT_CONSTANT_VOCAB_WIDGET_HEIGHT = 9 - -@cached -def _css_height_to_line_count(vreg): - cssprop = vreg.config.uiprops['facet_overflowedHeight'].lower().strip() - # let's talk a bit ... - # we try to deduce a number of displayed lines from a css property - # there is a linear (rough empiric coefficient == 0.73) relation between - # css _em_ value and line qty - # if we get another unit we're out of luck and resort to one constant - # hence, it is strongly advised not to specify but ems for this css prop - if cssprop.endswith('em'): - try: - return int(cssprop[:-2]) * .73 - except Exception: - vreg.warning('css property facet_overflowedHeight looks malformed (%r)', - cssprop) - return _DEFAULT_CONSTANT_VOCAB_WIDGET_HEIGHT +_DEFAULT_CONSTANT_VOCAB_WIDGET_HEIGHT = 12 class FacetVocabularyWidget(htmlwidgets.HTMLWidget): @@ -1364,13 +1348,35 @@ self.facet = facet self.items = [] + @property + @cached + def css_overflow_limit(self): + """ we try to deduce a number of displayed lines from a css property + if we get another unit we're out of luck and resort to one constant + hence, it is strongly advised not to specify but ems for this css prop + """ + vreg = self.facet._cw.vreg + cssprop = vreg.config.uiprops['facet_overflowedHeight'].lower().strip() + if cssprop.endswith('em'): + try: + return int(cssprop[:-2]) + except Exception: + vreg.warning('css property facet_overflowedHeight looks malformed (%r)', + cssprop) + return _DEFAULT_CONSTANT_VOCAB_WIDGET_HEIGHT + + @property @cached def height(self): - maxheight = _css_height_to_line_count(self.facet._cw.vreg) - return 1 + min(len(self.items), maxheight) + int(self.facet._support_and_compat()) + return 1 + min(len(self.items), + self.css_overflow_limit + int(self.facet._support_and_compat())) - def append(self, item): - self.items.append(item) + @property + @cached + def overflows(self): + return len(self.items) >= self.css_overflow_limit + + scrollbar_padding_factor = 4 def _render(self): w = self.w @@ -1380,29 +1386,52 @@ w(u'
%s
\n' % (xml_escape(self.facet.__regid__), title)) if self.facet._support_and_compat(): - _ = self.facet._cw._ - w(u'''''' % (xml_escape(self.facet.__regid__) + '_andor', _('and/or between different values'), - _('OR'), _('AND'))) - cssclass = 'facetBody' + self._render_and_or(w) + cssclass = 'vocabularyFacet' if not self.facet.start_unfolded: cssclass += ' hidden' - if len(self.items) > 6: - cssclass += ' overflowed' + overflow = self.overflows + if overflow: + if self.facet._support_and_compat(): + cssclass += ' vocabularyFacetBodyWithLogicalSelector' + else: + cssclass += ' vocabularyFacetBody' w(u'
\n' % cssclass) - for item in self.items: - item.render(w=w) + for value, label, selected in self.items: + if value is None: + continue + self._render_value(w, value, label, selected, overflow) w(u'
\n') w(u'\n') + def _render_and_or(self, w): + _ = self.facet._cw._ + w(u"""""" % (xml_escape(self.facet.__regid__) + '_andor', + _('and/or between different values'), + _('OR'), _('AND'))) + + def _render_value(self, w, value, label, selected, overflow): + cssclass = 'facetValue facetCheckBox' + if selected: + cssclass += ' facetValueSelected' + w(u'
\n' + % (cssclass, xml_escape(unicode(value)))) + # If it is overflowed one must add padding to compensate for the vertical + # scrollbar; given current css values, 4 blanks work perfectly ... + padding = u' ' * self.scrollbar_padding_factor if overflow else u'' + w('%s' % xml_escape(label)) + w(padding) + w(u'
') class FacetStringWidget(htmlwidgets.HTMLWidget): def __init__(self, facet): self.facet = facet self.value = None + @property def height(self): return 3 @@ -1447,6 +1476,7 @@ self.minvalue = minvalue self.maxvalue = maxvalue + @property def height(self): return 3 @@ -1507,34 +1537,6 @@ facet._cw.html_headers.define_var('DATE_FMT', fmt) -class FacetItem(htmlwidgets.HTMLWidget): - - selected_img = "black-check.png" - unselected_img = "no-check-no-border.png" - - def __init__(self, req, label, value, selected=False): - self._cw = req - self.label = label - self.value = value - self.selected = selected - - def _render(self): - w = self.w - cssclass = 'facetValue facetCheckBox' - if self.selected: - cssclass += ' facetValueSelected' - imgsrc = self._cw.data_url(self.selected_img) - imgalt = self._cw._('selected') - else: - imgsrc = self._cw.data_url(self.unselected_img) - imgalt = self._cw._('not selected') - w(u'
\n' - % (cssclass, xml_escape(unicode(self.value)))) - w(u'%s ' % (imgsrc, imgalt)) - w(u'%s' % xml_escape(self.label)) - w(u'
') - - class CheckBoxFacetWidget(htmlwidgets.HTMLWidget): selected_img = "black-check.png" unselected_img = "black-uncheck.png" @@ -1545,6 +1547,7 @@ self.value = value self.selected = selected + @property def height(self): return 2 @@ -1563,9 +1566,9 @@ imgalt = self._cw._('not selected') w(u'
\n' % (cssclass, xml_escape(unicode(self.value)))) - w(u'
') + w(u'
') w(u'%s ' % (imgsrc, imgalt)) - w(u'' + w(u'' % (xml_escape(self.facet.__regid__), title)) w(u'
\n') w(u'
\n')