diff --git a/doc/workbook/handling_request/form.md b/doc/workbook/handling_request/form.md index 8f6ded6d..6cdf2416 100644 --- a/doc/workbook/handling_request/form.md +++ b/doc/workbook/handling_request/form.md @@ -20,7 +20,7 @@ Nav: [Workbook](../workbook.md) :: [Basic Concepts](../basics/basics.md) :: [Han An HTML Form can handle `GET` and `POST` requests. When we use a form with method `GET`, the data is attached at the end of the url for example: ->http://wwww.example.com?key1=value1&...keyn=valuen +>http://wwww.example.com/?key1=value1&...keyn=valuen If we use the method `POST`, the data is sent to the server in a different line. diff --git a/doc/workbook/workbook.md b/doc/workbook/workbook.md index c454c49f..7b3183be 100644 --- a/doc/workbook/workbook.md +++ b/doc/workbook/workbook.md @@ -7,6 +7,7 @@ * [Handling Requests: Header Fields](#header_fields) * [Generating Responses](#generating_responses) * [Handling Cookies](#handling_cookies) +* [Web User Interface](#wui) * [EWF Deployment](#deployment) @@ -39,6 +40,11 @@ Before reading (or walking throught) the workbook, to get a quick overview of EW ## Generating Response [Generating Responses](./generating_response/generating_response.md) + + +## Web User Interface +[Web User Interface](./wui/readme.md) + ## Handling Cookies diff --git a/doc/workbook/wui/readme.md b/doc/workbook/wui/readme.md new file mode 100644 index 00000000..619d8986 --- /dev/null +++ b/doc/workbook/wui/readme.md @@ -0,0 +1,235 @@ +# Web User Interface (WUI) + +When it comes to web user interface, HTML, CSS and Javascript are part of the solution. +To generate the HTML and CSS, there are many solution, among them: +* use template (such as can use templates like the [Eiffel Smarty lib](https://github.com/EiffelSoftware/EiffelStudio/tree/master/Src/contrib/library/text/template/smarty) +* generate the html from the Eiffel code directly +* use high level components that generate the html themselves. + +Within the EiffelWeb framework, the `wsf_html` library provides classes to build a structured representation of html and then generate expected HTML. + + +## Overview +The [`wsf_html` library](https://github.com/EiffelWebFramework/EWF/tree/master/library/server/wsf_html) provides: + +* `CSS_*` classes to generate CSS style. +* `WSF_WIDGET_*` classes representing HTML widget (such as `HTML Response") + response.send (mesg) + end +``` + +In this case, the html is hardcoded, and written manually. +Now let's see how to use the WSF widgets on a more advanced usecase, a simple sign-in web form with 2 input fields `username`, `password` and `submit` button. + +```eiffel + + execute + do + -- Basic url dispatching... + if request.is_post_request_method then + execute_web_form_submission + else + execute_web_form_display + end + end + + execute_web_form_display + local + mesg: WSF_HTML_PAGE_RESPONSE + f: WSF_FORM + l_html: STRING + do + -- Build the web form + f := new_login_form + + -- Html generation + --| the first argument of `to_html` is the theme for advanced usage you can provide a custom theme + --| that can redefine how to generate link for instance. + l_html := f.to_html (create {WSF_REQUEST_THEME}.make_with_request (request)) + + -- Send the response + create mesg.make + mesg.set_title ("This is a Web form") + mesg.set_body (l_html) + response.send (mesg) + end + + execute_web_form_submission + do + ... this will be explain in next section ! + end + + new_login_form: WSF_FORM + local + f: WSF_FORM + f_set: WSF_FORM_FIELD_SET + f_username: WSF_FORM_TEXT_INPUT + f_password: WSF_FORM_PASSWORD_INPUT + f_submit: WSF_FORM_SUBMIT_INPUT + do + -- Build the web form + create f.make ("/form", "form-login") -- The form id is optional. + f.set_method_post -- it could also use the default GET method. + + -- Put specific html code + f.extend_html_text ("

Web form example

") + + -- username input field + create f_username.make ("username") + f_username.set_label ("User name") + f.extend (f_username) + + -- password input field + create f_password.make ("password") + f_password.set_label ("Password") + f.extend (f_password) + + -- submit button + create f_submit.make_with_text ("op", "Login") + f.extend (f_submit) + + Result := f + end +``` + +## Handling web form data + +When a form is submitted, the code can access the request data thanks to the `request: WSF_REQUEST` attribute. +See [Handling requests section](../handling_request/form.md) + +The `wsf_html` library, thanks to the `WSF_FORM`, provides a more powerful solution. +Indeed `WSF_HTML.process (request, .., ..)` analyze the request, fill the form fields, and process various validations, and submissions automatically. +see +```eiffel + process (req: WSF_REQUEST; a_before_callback, a_after_callback: detachable PROCEDURE [WSF_FORM_DATA]) + -- Process Current form with request `req`, + -- and build `last_data: WSF_FORM_DATA` object containing the field values. + -- agent `a_before_callback` is called before the validation + -- agent `a_after_callback` is called after the validation +``` + +In our previous sample code, `execute_web_form_submission` could be: +```eiffel + execute_web_form_submission + local + mesg: WSF_HTML_PAGE_RESPONSE + l_html: STRING + f: WSF_FORM + do + create mesg.make + mesg.set_title ("Web form submission") + create l_html.make_empty + + -- Build again the same form. + f := new_login_form + + -- Process this form with `request` data. + f.process (request, Void, Void) + if attached f.last_data as form_data and then not form_data.has_error then + -- `form_data` contains the submitted fields with their value. + + -- Depending on the form data, display a response. + l_html.append ("Username: ") + if attached form_data.string_item ("username") as l_username then + -- The username may contain unicode, or characters like '<' + -- thus, it needs to be html encoded + l_html.append (mesg.html_encoded_string (l_username)) + else + l_html.append ("missing value !!") + end + l_html.append ("
") + if attached form_data.string_item ("password") as l_password then + l_html.append ("Password: ") + -- The password may contain unicode, or characters like '<' + -- thus, it needs to be html encoded + l_html.append (mesg.html_encoded_string (l_password)) + else + l_html.append ("missing value !!") + end + l_html.append ("
") + else + l_html.append ("Error while processing the web form!") + + -- Display again the form (it will contain the submitted values, if any). + f.append_to_html (create {WSF_REQUEST_THEME}.make_with_request (request), l_html) + end + + mesg.set_body (l_html) + response.send (mesg) + end +``` + +In this case, the code could report an error if the username is empty, or with unwanted character ... this could be done in the `execute_web_form_submission` code, but it is also possible to set validation on each form field. +If those validations reports error, then the `form_data.has_error` will be True. + +To set validation, +For instance, in previous sample, accepts only alpha-numeric characters: +```eiffel + f_username.set_description ("Only alpha-numeric character are accepted.") + f_username.set_validation_action (agent (fd: WSF_FORM_DATA) + do + if attached fd.string_item ("username") as u then + if across u as ic some not ic.item.is_alpha_numeric end then + fd.report_invalid_field ("username", "Missing username value!") + elseif u.is_whitespace then + fd.report_invalid_field ("username", "Empty username value!") + end + else + fd.report_invalid_field ("username", "Missing username value!") + end + end) +``` +Notes: +* If the validation is not satisfied, the form data will report `has_error` as True, and the associated form will be updated with submitted values, and with `class="error"` set to the related html code. +The errors are also available via the `form_data.errors` feature. +* Since the validation feature argument is the `WSF_FORM_DATA` itself, it is also possible to validate several fields at once. + +If there is no error, the form submission process will trigger the `WSF_FORM.submit_actions`. This could be used when the form is built by different components, and each component will handle the submission of its associated fields. + + +## Catalog of widgets +The `wsf_html` library includes a collection of widgets and form items: + +### `WSF_WIDGET_...` +* `.._DIV`: `..` html element. +* `.._IMAGE`: `` html element. +* `.._RAW_TEXT`: html escaped text. +* `.._TEXT`: html text. +And more advanced widgets such as: +* `.._PAGER`: to generate pager widget like ` << 1 2 3 .. >> ` +* `.._TABLE`: a set of classes to generate `` html element. + + +### `WSF_FORM_...` +Widget usually included in web forms, such as +* `WSF_FORM_*_INPUT`: text, password, file, submit, ... +* `WSF_FORM_FIELD_SET`, `.._TEXTAREA` ... + + +## What about CSS style +The `CSS_STYLE`, `CSS_SELECTOR` and `CSS_TEXT` can be used to generate CSS style sheat, but they can also be used to set css style to `WSF_WIDGET`. + + +## Potential future evolutions +For now, the `wsf_html` provides a simple solution to build web form. It is always possible to add new `WSF_WIDGET` to support more html elements. +Advanced usage could be built on top of the `wsf_html` to include for instance javascript actions, but this is out of the scope of `wsf_html` . + + + diff --git a/library/server/wsf_html/form/wsf_form_field_set.e b/library/server/wsf_html/form/field/wsf_form_field_set.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_field_set.e rename to library/server/wsf_html/form/field/wsf_form_field_set.e diff --git a/library/server/wsf_html/form/wsf_form_textarea.e b/library/server/wsf_html/form/field/wsf_form_textarea.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_textarea.e rename to library/server/wsf_html/form/field/wsf_form_textarea.e diff --git a/library/server/wsf_html/form/wsf_form_button_input.e b/library/server/wsf_html/form/input/wsf_form_button_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_button_input.e rename to library/server/wsf_html/form/input/wsf_form_button_input.e diff --git a/library/server/wsf_html/form/wsf_form_checkbox_input.e b/library/server/wsf_html/form/input/wsf_form_checkbox_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_checkbox_input.e rename to library/server/wsf_html/form/input/wsf_form_checkbox_input.e diff --git a/library/server/wsf_html/form/wsf_form_color_input.e b/library/server/wsf_html/form/input/wsf_form_color_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_color_input.e rename to library/server/wsf_html/form/input/wsf_form_color_input.e diff --git a/library/server/wsf_html/form/wsf_form_date_input.e b/library/server/wsf_html/form/input/wsf_form_date_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_date_input.e rename to library/server/wsf_html/form/input/wsf_form_date_input.e diff --git a/library/server/wsf_html/form/wsf_form_datetime_input.e b/library/server/wsf_html/form/input/wsf_form_datetime_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_datetime_input.e rename to library/server/wsf_html/form/input/wsf_form_datetime_input.e diff --git a/library/server/wsf_html/form/wsf_form_datetime_local_input.e b/library/server/wsf_html/form/input/wsf_form_datetime_local_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_datetime_local_input.e rename to library/server/wsf_html/form/input/wsf_form_datetime_local_input.e diff --git a/library/server/wsf_html/form/wsf_form_email_input.e b/library/server/wsf_html/form/input/wsf_form_email_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_email_input.e rename to library/server/wsf_html/form/input/wsf_form_email_input.e diff --git a/library/server/wsf_html/form/wsf_form_file_input.e b/library/server/wsf_html/form/input/wsf_form_file_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_file_input.e rename to library/server/wsf_html/form/input/wsf_form_file_input.e diff --git a/library/server/wsf_html/form/wsf_form_hidden_input.e b/library/server/wsf_html/form/input/wsf_form_hidden_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_hidden_input.e rename to library/server/wsf_html/form/input/wsf_form_hidden_input.e diff --git a/library/server/wsf_html/form/wsf_form_image_input.e b/library/server/wsf_html/form/input/wsf_form_image_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_image_input.e rename to library/server/wsf_html/form/input/wsf_form_image_input.e diff --git a/library/server/wsf_html/form/wsf_form_month_input.e b/library/server/wsf_html/form/input/wsf_form_month_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_month_input.e rename to library/server/wsf_html/form/input/wsf_form_month_input.e diff --git a/library/server/wsf_html/form/wsf_form_number_input.e b/library/server/wsf_html/form/input/wsf_form_number_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_number_input.e rename to library/server/wsf_html/form/input/wsf_form_number_input.e diff --git a/library/server/wsf_html/form/wsf_form_password_input.e b/library/server/wsf_html/form/input/wsf_form_password_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_password_input.e rename to library/server/wsf_html/form/input/wsf_form_password_input.e diff --git a/library/server/wsf_html/form/wsf_form_radio_input.e b/library/server/wsf_html/form/input/wsf_form_radio_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_radio_input.e rename to library/server/wsf_html/form/input/wsf_form_radio_input.e diff --git a/library/server/wsf_html/form/wsf_form_range_input.e b/library/server/wsf_html/form/input/wsf_form_range_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_range_input.e rename to library/server/wsf_html/form/input/wsf_form_range_input.e diff --git a/library/server/wsf_html/form/wsf_form_reset_input.e b/library/server/wsf_html/form/input/wsf_form_reset_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_reset_input.e rename to library/server/wsf_html/form/input/wsf_form_reset_input.e diff --git a/library/server/wsf_html/form/wsf_form_search_input.e b/library/server/wsf_html/form/input/wsf_form_search_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_search_input.e rename to library/server/wsf_html/form/input/wsf_form_search_input.e diff --git a/library/server/wsf_html/form/wsf_form_selectable_input.e b/library/server/wsf_html/form/input/wsf_form_selectable_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_selectable_input.e rename to library/server/wsf_html/form/input/wsf_form_selectable_input.e diff --git a/library/server/wsf_html/form/wsf_form_submit_input.e b/library/server/wsf_html/form/input/wsf_form_submit_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_submit_input.e rename to library/server/wsf_html/form/input/wsf_form_submit_input.e diff --git a/library/server/wsf_html/form/wsf_form_tel_input.e b/library/server/wsf_html/form/input/wsf_form_tel_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_tel_input.e rename to library/server/wsf_html/form/input/wsf_form_tel_input.e diff --git a/library/server/wsf_html/form/wsf_form_text_input.e b/library/server/wsf_html/form/input/wsf_form_text_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_text_input.e rename to library/server/wsf_html/form/input/wsf_form_text_input.e diff --git a/library/server/wsf_html/form/wsf_form_time_input.e b/library/server/wsf_html/form/input/wsf_form_time_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_time_input.e rename to library/server/wsf_html/form/input/wsf_form_time_input.e diff --git a/library/server/wsf_html/form/wsf_form_url_input.e b/library/server/wsf_html/form/input/wsf_form_url_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_url_input.e rename to library/server/wsf_html/form/input/wsf_form_url_input.e diff --git a/library/server/wsf_html/form/wsf_form_week_input.e b/library/server/wsf_html/form/input/wsf_form_week_input.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_week_input.e rename to library/server/wsf_html/form/input/wsf_form_week_input.e diff --git a/library/server/wsf_html/form/wsf_form_select.e b/library/server/wsf_html/form/select/wsf_form_select.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_select.e rename to library/server/wsf_html/form/select/wsf_form_select.e diff --git a/library/server/wsf_html/form/wsf_form_select_option.e b/library/server/wsf_html/form/select/wsf_form_select_option.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_select_option.e rename to library/server/wsf_html/form/select/wsf_form_select_option.e diff --git a/library/server/wsf_html/form/wsf_form_selectable_item.e b/library/server/wsf_html/form/select/wsf_form_selectable_item.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_selectable_item.e rename to library/server/wsf_html/form/select/wsf_form_selectable_item.e diff --git a/library/server/wsf_html/form/widget/wsf_form_div.e b/library/server/wsf_html/form/widget/wsf_form_div.e new file mode 100644 index 00000000..58cd6dca --- /dev/null +++ b/library/server/wsf_html/form/widget/wsf_form_div.e @@ -0,0 +1,26 @@ +note + description : "Objects that ..." + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +class + WSF_FORM_DIV + +inherit + WSF_WIDGET_DIV + undefine + extend + end + + WSF_FORM_COMPOSITE + +create + make, + make_with_item, + make_with_items, + make_with_text, + make_with_text_and_css_id, + make_with_item_and_css_id + +end diff --git a/library/server/wsf_html/form/wsf_form_raw_text.e b/library/server/wsf_html/form/widget/wsf_form_raw_text.e similarity index 100% rename from library/server/wsf_html/form/wsf_form_raw_text.e rename to library/server/wsf_html/form/widget/wsf_form_raw_text.e diff --git a/library/server/wsf_html/form/wsf_form.e b/library/server/wsf_html/form/wsf_form.e index 0cf4d33e..9e6b51c0 100644 --- a/library/server/wsf_html/form/wsf_form.e +++ b/library/server/wsf_html/form/wsf_form.e @@ -8,16 +8,24 @@ class WSF_FORM inherit + WSF_WIDGET + WSF_FORM_COMPOSITE WSF_WITH_HTML_ATTRIBUTE + WSF_WITH_CSS_ID + + WSF_WITH_CSS_CLASS + + WSF_WITH_CSS_STYLE + create make feature {NONE} -- Initialization - make (a_action: READABLE_STRING_8; a_id: READABLE_STRING_8) + make (a_action: READABLE_STRING_8; a_id: detachable READABLE_STRING_8) do action := a_action id := a_id @@ -33,8 +41,8 @@ feature -- Access action: READABLE_STRING_8 -- URL for the web form - id: READABLE_STRING_8 - -- Id of the form + id: detachable READABLE_STRING_8 + -- Optional id of the form is_get_method: BOOLEAN do @@ -56,9 +64,10 @@ feature -- Access feature -- Basic operation process (req: WSF_REQUEST; a_before_callback, a_after_callback: detachable PROCEDURE [WSF_FORM_DATA]) - -- Process Current form with request `req' - -- agent `a_before_callback' is called before the validation - -- agent `a_after_callback' is called after the validation + -- Process Current form with request `req`, + -- and build `last_data: WSF_FORM_DATA` object containing the field values. + -- agent `a_before_callback` is called before the validation + -- agent `a_after_callback` is called after the validation local fd: WSF_FORM_DATA do @@ -120,22 +129,18 @@ feature -- Conversion local s: STRING_8 do - a_html.append ("%N") across items as c @@ -145,10 +150,4 @@ feature -- Conversion a_html.append ("%N") end - to_html (a_theme: WSF_THEME): STRING_8 - do - create Result.make_empty - append_to_html (a_theme, Result) - end - end diff --git a/library/server/wsf_html/widget/wsf_widget_agent_table.e b/library/server/wsf_html/widget/table/wsf_widget_agent_table.e similarity index 98% rename from library/server/wsf_html/widget/wsf_widget_agent_table.e rename to library/server/wsf_html/widget/table/wsf_widget_agent_table.e index 64d88cd3..05b835cc 100644 --- a/library/server/wsf_html/widget/wsf_widget_agent_table.e +++ b/library/server/wsf_html/widget/table/wsf_widget_agent_table.e @@ -1,6 +1,5 @@ note - description: "Summary description for {WSF_WIDGET_TABLE}." - author: "" + description: "Summary description for {WSF_WIDGET_AGENT_TABLE}." date: "$Date$" revision: "$Revision$" diff --git a/library/server/wsf_html/widget/wsf_widget_table.e b/library/server/wsf_html/widget/table/wsf_widget_table.e similarity index 100% rename from library/server/wsf_html/widget/wsf_widget_table.e rename to library/server/wsf_html/widget/table/wsf_widget_table.e diff --git a/library/server/wsf_html/widget/wsf_widget_table_column.e b/library/server/wsf_html/widget/table/wsf_widget_table_column.e similarity index 100% rename from library/server/wsf_html/widget/wsf_widget_table_column.e rename to library/server/wsf_html/widget/table/wsf_widget_table_column.e diff --git a/library/server/wsf_html/widget/wsf_widget_table_item.e b/library/server/wsf_html/widget/table/wsf_widget_table_item.e similarity index 100% rename from library/server/wsf_html/widget/wsf_widget_table_item.e rename to library/server/wsf_html/widget/table/wsf_widget_table_item.e diff --git a/library/server/wsf_html/widget/wsf_widget_table_iteration_cursor.e b/library/server/wsf_html/widget/table/wsf_widget_table_iteration_cursor.e similarity index 100% rename from library/server/wsf_html/widget/wsf_widget_table_iteration_cursor.e rename to library/server/wsf_html/widget/table/wsf_widget_table_iteration_cursor.e diff --git a/library/server/wsf_html/widget/wsf_widget_table_row.e b/library/server/wsf_html/widget/table/wsf_widget_table_row.e similarity index 100% rename from library/server/wsf_html/widget/wsf_widget_table_row.e rename to library/server/wsf_html/widget/table/wsf_widget_table_row.e diff --git a/library/server/wsf_html/form/wsf_form_div.e b/library/server/wsf_html/widget/wsf_widget_div.e similarity index 86% rename from library/server/wsf_html/form/wsf_form_div.e rename to library/server/wsf_html/widget/wsf_widget_div.e index 0d483669..e4709f22 100644 --- a/library/server/wsf_html/form/wsf_form_div.e +++ b/library/server/wsf_html/widget/wsf_widget_div.e @@ -5,15 +5,21 @@ note revision : "$Revision$" class - WSF_FORM_DIV + WSF_WIDGET_DIV inherit - WSF_FORM_ITEM + WSF_WIDGET - WSF_FORM_COMPOSITE + WSF_WIDGET_COMPOSITE WSF_WITH_CSS_ID + WSF_WITH_CSS_CLASS + + WSF_WITH_CSS_STYLE + + WSF_WITH_HTML_ATTRIBUTE + create make, make_with_item, @@ -71,14 +77,14 @@ feature -- Conversion append_css_class_to (a_html, Void) append_css_id_to (a_html) append_css_style_to (a_html) - - a_html.append (">%N") + append_html_attributes_to (a_html) + a_html.append (">") across items as c loop c.item.append_to_html (a_theme, a_html) end - a_html.append ("%N%N") + a_html.append ("%N") end end diff --git a/library/server/wsf_html/widget/wsf_widget_image.e b/library/server/wsf_html/widget/wsf_widget_image.e new file mode 100644 index 00000000..103b2752 --- /dev/null +++ b/library/server/wsf_html/widget/wsf_widget_image.e @@ -0,0 +1,97 @@ +note + description: "Summary description for {WSF_WIDGET_IMAGE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + WSF_WIDGET_IMAGE + +inherit + WSF_WIDGET + + WSF_WITH_CSS_ID + + WSF_WITH_CSS_CLASS + + WSF_WITH_CSS_STYLE + + WSF_WITH_HTML_ATTRIBUTE + +create + make + +feature {NONE} -- Initialization + + make (a_src: READABLE_STRING_8) + do + src := a_src + end + +feature -- Access + + src: READABLE_STRING_8 + -- `src` html attribute. + + + alt: detachable READABLE_STRING_8 + -- Alternate text for Current image. + + width: detachable READABLE_STRING_8 + -- Optional width value. + + height: detachable READABLE_STRING_8 + -- Optional height value. + +feature -- Change + + set_src (v: like src) + do + src := v + end + + set_alt (v: like alt) + do + alt := v + end + + set_width (v: like width) + do + width := v + end + + set_height (v: like height) + do + height := v + end + +feature -- Conversion + + append_to_html (a_theme: WSF_THEME; a_html: STRING_8) + do + a_html.append ("") + end + +end