Commit 81e97878 authored by Sylvain Thénault's avatar Sylvain Thénault
Browse files

[boxes] introduce new boxes system

* separate box content generation from its layout
* refactor css classes to allow moving boxes and still get consistent ui

On the way to contentnavigation/boxes unification (in a later patch)
* * *
some fixes for the previous (default_new_boxes_system) patch
* * *
some fixes for the previous (default_new_boxes_system) patch
parent f76599a96238
......@@ -903,7 +903,8 @@ class AutoPopulateTest(CubicWebTC):
for action in self.list_actions_for(rset):
yield InnerTest(self._testname(rset, action.__regid__, 'action'), self._test_action, action)
for box in self.list_boxes_for(rset):
yield InnerTest(self._testname(rset, box.__regid__, 'box'), box.render)
w = [].append
yield InnerTest(self._testname(rset, box.__regid__, 'box'), box.render, w)
@staticmethod
def _testname(rset, objid, objtype):
......
......@@ -60,9 +60,9 @@ In web/views/boxes.py lies the RSSIconBox class. Look at its selector:
.. sourcecode:: python
class RSSIconBox(ExtResourcesBoxTemplate):
class RSSIconBox(box.Box):
''' just display the RSS icon on uniform result set '''
__select__ = ExtResourcesBoxTemplate.__select__ & non_final_entity()
__select__ = box.Box.__select__ & non_final_entity()
It takes into account:
......@@ -1220,6 +1220,15 @@ def primary_view(cls, req, view=None, **kwargs):
return 1
@objectify_selector
@lltrace
def contextual(cls, req, view=None, **kwargs):
"""Return 1 if view's contextual property is true"""
if view is not None and view.contextual:
return 1
return 0
class match_view(ExpectedValueSelector):
"""Return 1 if a view is specified an as its registry id is in one of the
expected view id given to the initializer.
......@@ -1231,6 +1240,18 @@ class match_view(ExpectedValueSelector):
return 1
class match_context(ExpectedValueSelector):
@lltrace
def __call__(self, cls, req, context=None, **kwargs):
try:
if not context in self.expected:
return 0
except AttributeError:
return 1 # class doesn't care about search state, accept it
return 1
@objectify_selector
@lltrace
def match_context_prop(cls, req, context=None, **kwargs):
......
......@@ -15,9 +15,8 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""helper classes to generate simple (X)HTML tags
"""helper classes to generate simple (X)HTML tags"""
"""
__docformat__ = "restructuredtext en"
from cubicweb.uilib import simple_sgml_tag, sgml_attributes
......
......@@ -504,8 +504,13 @@ class ReloadableMixIn(object):
build_js = build_update_js_call # expect updatable component by default
@property
def domid(self):
return domid(self.__regid__)
@deprecated('[3.10] use .domid property')
def div_id(self):
return ''
return self.domid
class Component(ReloadableMixIn, View):
......@@ -513,14 +518,20 @@ class Component(ReloadableMixIn, View):
__registry__ = 'components'
__select__ = yes()
# XXX huummm, much probably useless
# XXX huummm, much probably useless (should be...)
htmlclass = 'mainRelated'
def div_class(self):
return '%s %s' % (self.htmlclass, self.__regid__)
@property
def cssclass(self):
return '%s %s' % (self.htmlclass, domid(self.__regid__))
# XXX a generic '%s%s' % (self.__regid__, self.__registry__.capitalize()) would probably be nicer
def div_id(self):
return '%sComponent' % self.__regid__
# XXX should rely on ReloadableMixIn.domid
@property
def domid(self):
return '%sComponent' % domid(self.__regid__)
@deprecated('[3.10] use .cssclass property')
def div_class(self):
return self.cssclass
class Adapter(AppObject):
......
......@@ -467,7 +467,7 @@ class VRegistry(dict):
self.load_module(module)
def load_module(self, module):
self.info('loading %s', module)
self.info('loading %s from %s', module.__name__, module.__file__)
if hasattr(module, 'registration_callback'):
module.registration_callback(self)
else:
......
......@@ -45,35 +45,31 @@ class Action(AppObject):
for action in self.actual_actions():
menu.append(box.box_action(action))
def url(self):
"""return the url associated with this action"""
raise NotImplementedError
def html_class(self):
if self._cw.selected(self.url()):
return 'selected'
if self.category:
return 'box' + self.category.capitalize()
def build_action(self, title, path, **kwargs):
return UnregisteredAction(self._cw, self.cw_rset, title, path, **kwargs)
def build_action(self, title, url, **kwargs):
return UnregisteredAction(self._cw, title, url, **kwargs)
def url(self):
"""return the url associated with this action"""
raise NotImplementedError
class UnregisteredAction(Action):
"""non registered action used to build boxes. Unless you set them
explicitly, .vreg and .schema attributes at least are None.
"""
"""non registered action, used to build boxes"""
category = None
id = None
def __init__(self, req, rset, title, path, **kwargs):
Action.__init__(self, req, rset=rset)
def __init__(self, req, title, url, **kwargs):
Action.__init__(self, req)
self.title = req._(title)
self._path = path
self._url = url
self.__dict__.update(kwargs)
def url(self):
return self._path
return self._url
class LinkToEntityAction(Action):
......
This diff is collapsed.
......@@ -197,7 +197,7 @@ class RelatedObjectsVComponent(EntityVComponent):
rset = self._cw.execute(self.rql(), {'x': eid})
if not rset.rowcount:
return
self.w(u'<div class="%s">' % self.div_class())
self.w(u'<div class="%s">' % self.cssclass)
self.w(u'<h4>%s</h4>\n' % self._cw._(self.title).capitalize())
self.wview(self.vid, rset)
self.w(u'</div>')
......
......@@ -230,7 +230,7 @@ td.calendar table {
.calendar th.month {
font-weight:bold;
padding-bottom:0.2em;
background: %(actionBoxTitleBgColor)s;
background: %(incontextBoxBodyBgColor)s;
}
.calendar th.month a{
......
......@@ -31,19 +31,22 @@ h1, h2, h3 { margin-top:0; margin-bottom:0; }
/* h3 { font-size:1.30769em; } */
/* scale traditional */
h1 { font-size: %(h1FontSize)s; }
h1,
.vtitle { font-size: %(h1FontSize)s; }
h2 { font-size: %(h2FontSize)s; }
h3 { font-size: %(h3FontSize)s; }
/* paddings */
h1 {
h1,
.vtitle {
border-bottom: %(h1BorderBottomStyle)s;
padding: %(h1Padding)s;
margin: %(h1Margin)s;
color: %(h1Color)s;
}
h1.plain {
h1.plain,
.vtitle {
border-bottom: none;
}
......@@ -100,7 +103,7 @@ ol {
}
ol ol,
ul ul{
ul ul {
margin-left: 8px;
margin-bottom : 0px;
}
......@@ -113,7 +116,7 @@ li {
margin-left: 1.5em;
}
img{
img {
border: none;
}
......@@ -139,7 +142,7 @@ input:focus {
border: 1px inset %(headerBgColor)s;
}
hr{
hr {
border: none;
border-bottom: 1px solid %(defaultColor)s;
height: 1px;
......@@ -234,16 +237,11 @@ table#header td#headtext {
/* Popup on login box and userActionBox */
div.popupWrapper {
position: relative;
z-index: 100;
}
div.popup {
position: absolute;
background: #fff;
/* background-color: #f0eff0; */
/* background-image: url(popup.png); */
/* background-repeat: repeat-x; */
/* background-positon: top left; */
border: 1px solid %(listingBorderColor)s;
border-top: none;
text-align: left;
......@@ -261,12 +259,13 @@ div#page {
margin: %(defaultLayoutMargin)s;
}
table#mainLayout #navColumnLeft {
table#mainLayout td#navColumnLeft {
width: 16em;
padding-right: %(defaultLayoutMargin)s;
}
table#mainLayout #navColumnRight {
table#mainLayout td#navColumnRight {
width: 16em;
padding-left: %(defaultLayoutMargin)s;
}
......@@ -301,28 +300,15 @@ div#contentmain{
color: %(defaultColor)s;
}
/* rql bar */
div#rqlinput {
margin-bottom: %(defaultLayoutMargin)s;
}
input#rql{
padding: 0.25em 0.3em;
width: 99%;
}
/* boxes */
/* XXX old boxes, deprecated */
div.boxFrame {
width: 100%;
}
div.boxTitle {
overflow: hidden;
font-weight: bold;
color: #fff;
background: %(boxTitleBg)s;
background: %(contextualBoxTitleBgColor)s;
}
div.boxTitle span,
......@@ -331,14 +317,7 @@ div.sideBoxTitle span {
white-space: nowrap;
}
div.searchBoxFrame div.boxTitle,
div.greyBoxFrame div.boxTitle {
background: %(actionBoxTitleBg)s;
}
div.sideBoxTitle span,
div.searchBoxFrame div.boxTitle span,
div.greyBoxFrame div.boxTitle span {
div.sideBoxTitle span {
color: %(defaultColor)s;
}
......@@ -352,34 +331,13 @@ div.boxContent {
border-top: none;
}
a.boxMenu {
display: block;
padding: 1px 9px 1px 3px;
background: transparent %(bulletDownImg)s;
}
a.boxMenu:hover {
background: %(sideBoxBodyBgColor)s %(bulletDownImg)s;
cursor: pointer;
}
a.popupMenu {
background: transparent url("puce_down_black.png") 2% 6px no-repeat;
padding-left: 2em;
}
div.searchBoxFrame div.boxContent {
padding: 4px 4px 3px;
background: #f0eff0 url("gradient-grey-up.png") left top repeat-x;
}
div.shadow{
height: 14px;
background: url("shadow.gif") no-repeat top right;
}
div.sideBoxTitle {
background: %(actionBoxTitleBg)s;
background: %(incontextBoxBodyBg)s;
display: block;
font-weight: bold;
}
......@@ -400,11 +358,11 @@ ul.sideBox li{
div.sideBoxBody {
padding: 0.2em 5px;
background: %(sideBoxBodyBg)s;
background: %(incontextBoxBodyBg)s;
}
div.sideBoxBody a {
color: %(sideBoxBodyColor)s;
color: %(incontextBoxBodyColor)s;
}
div.sideBoxBody a:hover {
......@@ -415,6 +373,174 @@ div.sideBox table td {
padding-right: 1em;
}
/* boxes */
div.boxTitle {
overflow: hidden;
font-weight: bold;
}
div.boxTitle span {
padding: 0px 0.5em;
white-space: nowrap;
}
div.boxBody {
padding: 5px;
border-top: none;
background-color: %(leftrightBoxBodyBgColor)s;
}
div.boxBody a {
color: %(leftrightBoxBodyColor)s;
}
div.boxBody a:hover {
text-decoration: none;
cursor: pointer;
background-color: %(leftrightBoxBodyHoverBgColor)s;
}
/* boxes contextual customization */
.contextFreeBox div.boxTitle {
background: %(contextFreeBoxTitleBg)s;
color: %(contextFreeBoxTitleColor)s;
}
.contextualBox div.boxTitle {
background: %(contextualBoxTitleBg)s;
color: %(contextualBoxTitleColor)s;
}
.primaryRight div.boxTitle {
background: %(incontextBoxTitleBg)s;
color: %(incontextBoxTitleColor)s;
}
.primaryRight div.boxBody {
padding: 0.2em 5px;
background: %(incontextBoxBodyBgColor)s;
}
.primaryRight div.boxBody a {
color: %(incontextBoxBodyColor)s;
}
.primaryRight div.boxBody a:hover {
background-color: %(incontextBoxBodyHoverBgColor)s;
}
.primaryRight div.boxFooter {
margin-bottom: 1em;
}
#navColumnLeft div.boxFooter, #navColumnRight div.boxFooter{
height: 14px;
background: url("shadow.gif") no-repeat top right;
}
/* boxes lists and menus */
ul.boxListing {
margin: 0;
padding: 0;
}
ul.boxListing ul {
padding: 1px 3px;
}
ul.boxListing a {
color: %(defaultColor)s;
display: block;
padding: 1px 9px 1px 3px;
}
ul.boxListing li {
margin: 0px;
padding: 0px;
background-image: none;
}
ul.boxListing ul li {
margin: 0px;
padding-left: 8px;
}
ul.boxListing ul li a {
padding-left: 10px;
background-image: url("bullet_orange.png");
background-repeat: no-repeat;
background-position: 0 6px;
}
ul.boxListing .selected {
color: %(aColor)s;
font-weight: bold;
}
ul.boxListing a.boxMenu:hover {
border-top: medium none;
background: %(leftrightBoxBodyHoverBgColor)s;
}
a.boxMenu,
ul.boxListing a.boxMenu{
display: block;
padding: 1px 3px;
background: transparent %(bulletDownImg)s;
}
ul.boxListing a.boxMenu:hover {
border-top: medium none;
background: %(leftrightBoxBodyHoverBgColor)s %(bulletDownImg)s;
}
a.boxMenu:hover {
cursor: pointer;
}
a.popupMenu {
background: transparent url("puce_down_black.png") 2% 6px no-repeat;
padding-left: 2em;
}
/* custom boxes */
.search_box div.boxBody {
padding: 4px 4px 3px;
background: #f0eff0 url("gradient-grey-up.png") left top repeat-x;
}
.bookmarks_box ul.boxListing div a{
background: #fff;
display: inline;
padding: 0;
}
.bookmarks_box ul.boxListing div a:hover{
border-bottom: 1px solid #000;
}
.download_box div.boxTitle {
background : #8fbc8f !important;
}
.download_box div.boxBody {
background : #eefed9;
}
/* search box and rql bar */
div#rqlinput {
margin-bottom: %(defaultLayoutMargin)s;
}
input#rql{
padding: 0.25em 0.3em;
width: 99%;
}
input.rqlsubmit{
display: block;
width: 20px;
......@@ -424,7 +550,7 @@ input.rqlsubmit{
}
input#norql{
width:13em;
width:155px;
margin-right: 2px;
}
......@@ -435,7 +561,7 @@ a.logout, a.logout:visited, a.logout:hover{
}
div#userActionsBox {
width: 14em;
width: 15em;
text-align: right;
}
......@@ -445,20 +571,6 @@ div#userActionsBox a.popupMenu {
padding-right: 2em;
}
/* download box XXX move to its own file? */
div.downloadBoxTitle{
background : #8fbc8f;
font-weight: bold;
}
div.downloadBox{
font-weight: bold;
}
div.downloadBox div.sideBoxBody{
background : #eefed9;
}
/**************/
/* navigation */
/**************/
......@@ -565,7 +677,7 @@ div.toolbarButton {
div#appMsg {
margin-bottom: %(defaultLayoutMargin)s;
border: 1px solid %(actionBoxTitleBgColor)s;
border: 1px solid %(incontextBoxTitleBgColor)s;
}
.message {
......@@ -578,7 +690,7 @@ div#appMsg {
padding-left: 25px;
background: %(msgBgColor)s url("critical.png") 2px center no-repeat;
color: %(errorMsgColor)s;
border: 1px solid %(actionBoxTitleBgColor)s;
border: 1px solid %(incontextBoxTitleBgColor)s;
}
/* search-associate message */
......@@ -733,7 +845,7 @@ div#newvalue{
input.button{
margin: 1em 1em 0px 0px;
border: 1px solid %(buttonBorderColor)s;
border-color: %(buttonBorderColor)s %(actionBoxTitleBgColor)s %(actionBoxTitleBgColor)s %(buttonBorderColor)s;
border-color: %(buttonBorderColor)s %(incontextBoxTitleBgColor)s %(incontextBoxTitleBgColor)s %(buttonBorderColor)s;
}
/* FileItemInnerView jquery.treeview.css */
......@@ -753,74 +865,20 @@ ul.startup {
ul.startup li,
ul.section li {
margin-left:0px
}
ul.boxListing {
margin: 0px;
padding: 0px 3px;
}
ul.boxListing li,
ul.boxListing ul li {
margin: 0px;
padding: 0px;
background-image: none;
}
ul.boxListing ul {
padding: 1px 3px;
}
ul.boxListing a {
color: %(defaultColor)s;
display:block;
padding: 1px 9px 1px 3px;
}
ul.boxListing .selected {
color: %(aColor)s;
font-weight: bold;
}
ul.boxListing a.boxMenu:hover {
border-top: medium none;
background: %(sideBoxBodyBgColor)s %(bulletDownImg)s;
}
ul.boxListing a.boxBookmark {