You can add CSS classes and styles to your fields like so:
phone = TextField(label='Phone',
validator=twc.Required,
css_class='MyTextFieldClass',
attrs=dict(style='display:block;width:8em', maxlength='12'))
For a completely different layout, you need to subclass BaseLayout
and reference your own template as explained here: http://tw2core.readthedocs.org/en/latest/design/#template.
For instance, I have created a more flexible Layout class called FloatLayout:
from itertools import groupby
from tw2.core import Param
from tw2.forms.widgets import BaseLayout
class FloatLayout(BaseLayout):
template = "widgets.templates.float_layout"
aside = Param('All fields aside', default=False)
def rows(self, children):
"""Create the rows."""
def row_no(child, no=[0]):
if not self.aside and not getattr(child, 'aside', False):
no[0] += 1
return no[0]
return groupby(children, row_no)
It can be used with this FloatForm class:
from tw2.core import Variable
from tw2.forms import Form
class FloatForm(Form):
"""Form using floating divisions allowing multiple fields per row.
Fields having the 'aside' attribute set appear on the same row.
Something like the following should be included in the site CSS file:
form.floatform {
margin-bottom: 1ex;
}
form.floatform div.row {
clear: left;
overflow: hidden;
height: 100%;
margin-top: 1.5ex;
}
form.floatform div.field {
float: left;
margin-right: 1em;
}
form.floatform label.fieldlabel {
display: block;
}
form.floatform div.submit {
margin-top: 3ex;
}
"""
template = "widgets.templates.float_form"
child = Variable(default=FloatLayout)
css_class = "floatform"
The Genshi template float_layout.html for the FloatLayout is this:
<div xmlns:py="http://genshi.edgewall.org/" py:attrs="w.attrs" py:strip="True">
<div py:for="row_no, row in w.rows(w.children_non_hidden)"
class="${row_no % 2 and 'odd' or 'even'} row">
<div py:for="child in row" py:attrs="child.container_attrs"
class="field${child.validator and
getattr(child.validator, 'required', None) and ' required' or ''}"
title="${w.hover_help and w.help_text or ''}">
<label py:if="child.label" for="${child.attrs.get('id')}"
class="fieldlabel" py:content="child.label"/>
<span py:replace="child.display()"/>
<span py:if="not w.hover_help and child.help_text"
class="fieldhelp" py:content="child.help_text"/>
<span py:if="child.error_msg"
class="fielderror" py:content="child.error_msg"/>
</div>
</div>
<div py:if="w.children_hidden" style="display:none">
<div py:for="child in w.children_hidden" py:replace="child.display()"/>
</div>
</div>
The Genshi template float_form.html for the FloatForm is this:
<form xmlns:py="http://genshi.edgewall.org/"
class="floatform" py:attrs="w.attrs">
<div py:if="w.error_msg" class="formerror" py:content="w.error_msg"/>
<div py:if="w.help_msg" class="formhelp"><p py:content="w.help_msg"/></div>
<div py:replace="w.child.display()"/>
<div py:for="button in w.buttons" class="field" py:content="button.display()"/>
</form>
A concrete Form could now look like this:
class UserForm(FloatForm):
action = url('save_user')
submit = SubmitButton('Save user')
user_id = HiddenField(validator=IntValidator())
user_name = TextField(validator=UserNameValidator(max=16),
size=20, maxlength=16, label=u'User name:')
remote_account = CheckBox(validator=BoolValidator(),
label=u'Remote account:', aside=True)
new_password = PasswordField(
validator=PasswordValidator(required=False),
size=20, maxlength=16, label=u'Password:', aside=True)
group_id = CheckBoxList(item_validator=IntValidator(),
label=u'Roles:', css_class='inline')
display_name = TextField(validator=NameValidator(max=255),
size=64, maxlength=255, label=u'Real name:')
mail = TextField(validator=EmailIfLocalValidator(),
size=64, maxlength=255, label=u'Email address:')
As you see, the fields remote_account
, new_password
have an attribute aside
which causes them to appear on the same line as user_name
.