mirror of
https://github.com/taigrr/arduinolibs
synced 2025-01-18 04:33:12 -08:00
309 lines
7.7 KiB
C++
309 lines
7.7 KiB
C++
/*
|
|
* Copyright (C) 2012 Southern Storm Software, Pty Ltd.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "Form.h"
|
|
#include "Field.h"
|
|
|
|
/**
|
|
* \class Form Form.h <Form.h>
|
|
* \brief Manager for a form containing data input/output fields.
|
|
*
|
|
* See the \ref lcd_form "Form example" for more information on
|
|
* creating an application that uses forms and fields.
|
|
*/
|
|
|
|
/**
|
|
* \brief Constructs a new form and associates it with \a lcd.
|
|
*
|
|
* This constructor is typically followed by calls to construct Field
|
|
* values for each of the fields on the form. For example:
|
|
*
|
|
* \code
|
|
* Form mainForm(lcd);
|
|
* TextField welcomeField(mainForm, "Form example", "v1.0");
|
|
* \endcode
|
|
*
|
|
* \image html FormText.png
|
|
*/
|
|
Form::Form(LiquidCrystal &lcd)
|
|
: _lcd(&lcd)
|
|
, first(0)
|
|
, last(0)
|
|
, current(0)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* \brief Detaches all remaining fields and destroys this form.
|
|
*/
|
|
Form::~Form()
|
|
{
|
|
Field *field = first;
|
|
Field *next;
|
|
while (field != 0) {
|
|
next = field->next;
|
|
field->_form = 0;
|
|
field->next = 0;
|
|
field->prev = 0;
|
|
field = next;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Dispatches \a event to the currently active field using
|
|
* Field::dispatch().
|
|
*
|
|
* The \a event is usually obtained from FreetronicsLCD::getButton().
|
|
*
|
|
* Returns zero if the \a event has been handled and no further action
|
|
* is required.
|
|
*
|
|
* Returns FORM_CHANGED if one of the fields on the form has changed value
|
|
* due to the \a event, perhaps requiring the application to take further
|
|
* action based on the new field value. Use currentField() or isCurrent()
|
|
* to determine which field has changed.
|
|
*
|
|
* \code
|
|
* int event = lcd.getButton();
|
|
* if (mainForm.dispatch(event) == FORM_CHANGED) {
|
|
* if (mainForm.isCurrent(volumeField)) {
|
|
* // Adjust the volume to match the field.
|
|
* setVolume(volumeField.value());
|
|
* }
|
|
* }
|
|
* \endcode
|
|
*
|
|
* This function handles the Left and Right buttons to navigate between fields.
|
|
*
|
|
* \sa Field::dispatch(), FreetronicsLCD::getButton(), currentField(), isCurrent()
|
|
*/
|
|
int Form::dispatch(int event)
|
|
{
|
|
if (current) {
|
|
int exitval = current->dispatch(event);
|
|
if (exitval >= 0)
|
|
return exitval;
|
|
}
|
|
if (event == LCD_BUTTON_LEFT)
|
|
prevField();
|
|
else if (event == LCD_BUTTON_RIGHT)
|
|
nextField();
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Changes to the next field in the "tab order".
|
|
*
|
|
* \sa prevField(), defaultField(), currentField()
|
|
*/
|
|
void Form::nextField()
|
|
{
|
|
Field *field = current;
|
|
if (!field)
|
|
field = first;
|
|
if (field && field->next)
|
|
field = field->next;
|
|
else
|
|
field = first;
|
|
setCurrentField(field);
|
|
}
|
|
|
|
/**
|
|
* \brief Changes to the previous field in the "tab order".
|
|
*
|
|
* \sa nextField(), defaultField(), currentField()
|
|
*/
|
|
void Form::prevField()
|
|
{
|
|
Field *field = current;
|
|
if (!field)
|
|
field = last;
|
|
if (field && field->prev)
|
|
field = field->prev;
|
|
else
|
|
field = last;
|
|
setCurrentField(field);
|
|
}
|
|
|
|
/**
|
|
* \brief Changes to default field (i.e., the first field).
|
|
*
|
|
* \sa nextField(), prevField(), currentField()
|
|
*/
|
|
void Form::defaultField()
|
|
{
|
|
setCurrentField(first);
|
|
}
|
|
|
|
/**
|
|
* \brief Adds \a field to this form.
|
|
*
|
|
* Usually this function is not required because the field's constructor
|
|
* will add the field to the form automatically.
|
|
*
|
|
* \sa removeField()
|
|
*/
|
|
void Form::addField(Field *field)
|
|
{
|
|
if (field->_form)
|
|
return; // Already added to a form.
|
|
field->_form = this;
|
|
field->next = 0;
|
|
field->prev = last;
|
|
if (last)
|
|
last->next = field;
|
|
else
|
|
first = field;
|
|
last = field;
|
|
}
|
|
|
|
/**
|
|
* \brief Removes \a field from this form.
|
|
*
|
|
* If \a field is the current field on-screen, then either the next or
|
|
* previous field will be made current.
|
|
*
|
|
* \sa addField()
|
|
*/
|
|
void Form::removeField(Field *field)
|
|
{
|
|
if (field->_form != this)
|
|
return; // Not a member of this form.
|
|
if (current == field) {
|
|
if (field->next)
|
|
setCurrentField(field->next);
|
|
else if (field->prev)
|
|
setCurrentField(field->prev);
|
|
else
|
|
setCurrentField(0);
|
|
}
|
|
if (field->next)
|
|
field->next->prev = field->prev;
|
|
else
|
|
last = field->prev;
|
|
if (field->prev)
|
|
field->prev->next = field->next;
|
|
else
|
|
first = field->next;
|
|
field->_form = 0;
|
|
field->next = 0;
|
|
field->prev = 0;
|
|
}
|
|
|
|
/**
|
|
* \fn Field *Form::currentField() const
|
|
* \brief Returns the current field that is displayed on-screen.
|
|
*
|
|
* Returns null if the form has no fields, or setCurrentField() explicitly
|
|
* set the current field to null.
|
|
*
|
|
* \sa setCurrentField(), isCurrent()
|
|
*/
|
|
|
|
/**
|
|
* \brief Sets the current \a field that is displayed on-screen.
|
|
*
|
|
* Use this function to programmatically force the form to display a
|
|
* specific field on-screen.
|
|
*
|
|
* \sa currentField(), isCurrent()
|
|
*/
|
|
void Form::setCurrentField(Field *field)
|
|
{
|
|
if (field && field->_form != this)
|
|
return; // Wrong form.
|
|
if (visible) {
|
|
bool reverse = false;
|
|
if (current) {
|
|
current->exitField();
|
|
if (field->next == current)
|
|
reverse = true;
|
|
else if (!field->next && current == first)
|
|
reverse = true;
|
|
}
|
|
current = field;
|
|
_lcd->clear();
|
|
if (current)
|
|
current->enterField(reverse);
|
|
} else {
|
|
current = field;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \fn bool Form::isCurrent(Field &field) const
|
|
* \brief Returns true if \a field is currently displayed on-screen, false otherwise.
|
|
*
|
|
* This function is typically called after dispatch() returns FORM_CHANGED
|
|
* to determine which field has changed.
|
|
*
|
|
* \sa currentField(), setCurrentField()
|
|
*/
|
|
|
|
/**
|
|
* \brief Shows the form, or does nothing if the form is already on-screen.
|
|
*
|
|
* When the form is shown, the screen will be cleared and the currentField()
|
|
* will be drawn.
|
|
*
|
|
* If the form was previously hidden, then the field that was previously
|
|
* current will be shown again. Call defaultField() before show() to reset
|
|
* the form to show the first field instead.
|
|
*
|
|
* \sa hide(), isVisible(), defaultField()
|
|
*/
|
|
void Form::show()
|
|
{
|
|
if (!visible) {
|
|
if (!current)
|
|
current = first;
|
|
visible = true;
|
|
_lcd->clear();
|
|
if (current)
|
|
current->enterField(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Hides the form, or does nothing if the form is not on-screen.
|
|
*
|
|
* The screen will be cleared to remove the contents of the current field.
|
|
*
|
|
* \sa show(), isVisible()
|
|
*/
|
|
void Form::hide()
|
|
{
|
|
if (visible) {
|
|
if (current)
|
|
current->exitField();
|
|
visible = false;
|
|
_lcd->clear();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \fn bool Form::isVisible() const
|
|
* \brief Returns true if the form is shown; false if the form is hidden.
|
|
*
|
|
* \sa show(), hide()
|
|
*/
|