summaryrefslogtreecommitdiff
path: root/boards/ek-lm4f232/drivers/slidemenuwidget.c
diff options
context:
space:
mode:
authorYuval Adam <yuv.adm@gmail.com>2014-06-29 12:34:32 +0300
committerYuval Adam <yuv.adm@gmail.com>2014-06-29 12:34:32 +0300
commitc3e4c9a25c2910d2d66d52215b3406b13d5b23d5 (patch)
treeadded370d1e356901f8579f076e3263fb0464db7 /boards/ek-lm4f232/drivers/slidemenuwidget.c
parent990090a4cc9070837d31e66b58d40f0c3d038741 (diff)
Add more board models
Diffstat (limited to 'boards/ek-lm4f232/drivers/slidemenuwidget.c')
-rw-r--r--boards/ek-lm4f232/drivers/slidemenuwidget.c1424
1 files changed, 1424 insertions, 0 deletions
diff --git a/boards/ek-lm4f232/drivers/slidemenuwidget.c b/boards/ek-lm4f232/drivers/slidemenuwidget.c
new file mode 100644
index 0000000..805b77b
--- /dev/null
+++ b/boards/ek-lm4f232/drivers/slidemenuwidget.c
@@ -0,0 +1,1424 @@
+//*****************************************************************************
+//
+// slidemenuwidget.c - A sliding menu drawing widget.
+//
+// Copyright (c) 2011-2014 Texas Instruments Incorporated. All rights reserved.
+// Software License Agreement
+//
+// Texas Instruments (TI) is supplying this software for use solely and
+// exclusively on TI's microcontroller products. The software is owned by
+// TI and/or its suppliers, and is protected under applicable copyright
+// laws. You may not combine this software with "viral" open-source
+// software in order to form a larger program.
+//
+// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
+// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
+// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
+// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+// DAMAGES, FOR ANY REASON WHATSOEVER.
+//
+// This is part of revision 2.1.0.12573 of the EK-LM4F232 Firmware Package.
+//
+//*****************************************************************************
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "inc/hw_types.h"
+#include "driverlib/debug.h"
+#include "utils/uartstdio.h"
+#include "grlib/grlib.h"
+#include "grlib/widget.h"
+#include "slidemenuwidget.h"
+
+//*****************************************************************************
+//
+//! \addtogroup slidemenuwidget_api
+//! @{
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// This is a custom widget for drawing a menu system on the display. The
+// widget presents the menus using a "sliding" animation. The menu items
+// are shown in a vertical list, and as the user scrolls through the list
+// of menu items, the menu slides up and down the display. When a menu item
+// is selected to descend in the menu tree, the widget slides the old menu
+// off the to left while the new menu slides in from the right. Likewise,
+// going up in the menu tree, the higher level menu slides back onto the
+// screen from the left.
+//
+// Additional structures are provided to implement a menu, and menu items.
+// Each menu contains menu items, and each menu item can have a child menu.
+// These structures can be used to build a menu tree. The menu widget will
+// show one menu at any given time, the menu that is displayed on the screen.
+//
+// In addition to child menus, any menu item can have instead a child widget.
+// If this is used, then when the user selects a menu item, a new widget can
+// be activated to perform some function. When the function of the child
+// widget completes, then the widget slides back off the screen (to the right)
+// and the parent menu is displayed again.
+//
+// A given menu can have menu items that are individually selectable or
+// multiple-selectable. For individually selectable menu items, the item
+// is selected by leaving the menu with the focus on the selected item. For
+// example navigating down to a submenu with choices A, B and C, and then
+// navigating until the focus is on item B will cause item B to be selected.
+// The menu will remember that item B was selected even when navigating away
+// from that menu.
+//
+// If a menu is configured to be multiple-selectable, then each menu item has
+// a check box that is checked by pressing the select button. When the item
+// is selected the box will show an X. Any or all or none can be selected in
+// this way. When a menu is configured to be multi-selectable, the menu items
+// cannot have any child menus or widgets.
+//
+// The menu widget provides some visual clues to the user about how to
+// navigate the menu tree. Whenever a menu item has a child menu or child
+// widget, then a small right arrow is shown on the right side of the menu
+// item that has the focus. This tells the user to press the "right" button
+// to descend to the next menu or widget. When it is possible to go up a
+// level in the menu tree (when showing a child menu), a small left arrow
+// will be shown on the menu item with the focus. This is an indication to the
+// user that they should press the "left" button.
+//
+// This widget is meant to work with key/button presses. It expects there
+// to be up/down/left/right and select buttons. The widget will need to be
+// modified in order to work with a pointer input.
+//
+// In order to perform the sliding animation, the menu widget requires that
+// it be provided with two off-screen displays. The menu widget renders the
+// two menus (the old and the new) into the two buffers, and then repeatedly
+// paints both to the physical display while adjusting the coordinates as
+// appropriate. This will cause the menus to appear animated and move across
+// the display. When the menus are being animated, the menu widget is taking
+// all the non-interrupt processor time in order to draw the buffers to the
+// display. This operation occurs in response to the widget processing of the
+// key/button events and will occur in the thread that calls
+// WidgetMessageQueueProcess(). The programmer should be aware of this
+// processing burden when designing an application that uses the sliding
+// menu widget.
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// A graphics image of a small right arrow icon.
+//
+//*****************************************************************************
+const uint8_t g_ui8RtArrow[] =
+{
+ IMAGE_FMT_1BPP_UNCOMP,
+ 4, 0,
+ 8, 0,
+
+ 0x80,
+ 0xC0,
+ 0xE0,
+ 0xF0,
+ 0xE0,
+ 0xC0,
+ 0x80,
+ 0
+};
+
+//*****************************************************************************
+//
+// A graphics image of a small left arrow icon.
+//
+//*****************************************************************************
+const uint8_t g_ui8LtArrow[] =
+{
+ IMAGE_FMT_1BPP_UNCOMP,
+ 4, 0,
+ 8, 0,
+
+ 0x10,
+ 0x30,
+ 0x70,
+ 0xF0,
+ 0x70,
+ 0x30,
+ 0x10,
+ 0
+};
+
+//*****************************************************************************
+//
+// A graphics image of a small unchecked box icon.
+//
+//*****************************************************************************
+const uint8_t g_ui8Unchecked[] =
+{
+ IMAGE_FMT_1BPP_UNCOMP,
+ 7, 0,
+ 8, 0,
+
+ 0xFE,
+ 0x82,
+ 0x82,
+ 0x82,
+ 0x82,
+ 0x82,
+ 0xFE,
+ 0
+};
+
+//*****************************************************************************
+//
+// A graphics image of a small checked box icon.
+//
+//*****************************************************************************
+const uint8_t g_ui8Checked[] =
+{
+ IMAGE_FMT_1BPP_UNCOMP,
+ 7, 0,
+ 8, 0,
+
+ 0xFE,
+ 0xC6,
+ 0xAA,
+ 0x92,
+ 0xAA,
+ 0xC6,
+ 0xFE,
+ 0
+};
+
+//*****************************************************************************
+//
+//! Draws the current menu into a drawing context, off-screen buffer.
+//!
+//! \param psMenuWidget points at the SlideMenuWidget being processed.
+//! \param psContext points to the context where all drawing should be done.
+//! \param i32OffsetY is the Y offset for drawing the menu.
+//!
+//! This function renders a menu (set of menu items), into a drawing context.
+//! It assumes that the drawing context is an off-screen buffer, and that
+//! the entire buffer belongs to this widget. The vertical position of the
+//! menu can be adjusted by using the parameter i32OffsetY. This value can be
+//! positive or negative and can cause the menu to be rendered above or below
+//! the normal position in the display.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+SlideMenuDraw(tSlideMenuWidget *psMenuWidget, tContext *psContext,
+ int32_t i32OffsetY)
+{
+ tSlideMenu *psMenu;
+ uint32_t ui32Idx;
+ tRectangle sRect;
+
+ //
+ // Check the arguments
+ //
+ ASSERT(psMenuWidget);
+ ASSERT(psContext);
+
+ //
+ // Set the foreground color for the rectangle fill to match what we want
+ // as the menu background.
+ //
+ GrContextForegroundSet(psContext, psMenuWidget->ui32ColorBackground);
+ GrRectFill(psContext, &psContext->sClipRegion);
+
+ //
+ // Get the current menu that is being displayed
+ //
+ psMenu = psMenuWidget->psSlideMenu;
+
+ //
+ // Set the foreground to the color we want for the menu item boundaries
+ // and text color, text font.
+ //
+ GrContextForegroundSet(psContext, psMenuWidget->ui32ColorForeground);
+ GrContextFontSet(psContext, psMenuWidget->psFont);
+
+ //
+ // Set the rectangle bounds for the first menu item.
+ // The starting Y value is calculated based on which menu item is currently
+ // centered. Y coordinates are subtracted to find the Y start location
+ // of the first menu item, which could even be off the display.
+ //
+ // Set the X coords of the menu item to the extents of the display
+ //
+ sRect.i16XMin = 0;
+ sRect.i16XMax = psContext->sClipRegion.i16XMax;
+
+ //
+ // Find the Y coordinate of the centered menu item
+ //
+ sRect.i16YMin = (psContext->psDisplay->ui16Height / 2) -
+ (psMenuWidget->ui32MenuItemHeight / 2);
+
+ //
+ // Adjust to find Y coordinate of first menu item
+ //
+ sRect.i16YMin -= psMenu->ui32CenterIndex * psMenuWidget->ui32MenuItemHeight;
+
+ //
+ // Now adjust for the offset that was passed in by caller. This allows
+ // for drawing menu items above or below the main display.
+ //
+ sRect.i16YMin += i32OffsetY;
+
+ //
+ // Find the ending Y coordinate of first menu item
+ //
+ sRect.i16YMax = sRect.i16YMin + psMenuWidget->ui32MenuItemHeight - 1;
+
+ //
+ // Start the index at the first menu item. It is possible that this
+ // menu item is off the display.
+ //
+ ui32Idx = 0;
+
+ //
+ // Loop through all menu items, drawing on the display. Note that some
+ // may not be on the screen, but they will be clipped.
+ //
+ while(ui32Idx < psMenu->ui32Items)
+ {
+ //
+ // If this index is the one that is highlighted, then change the
+ // background
+ //
+ if(ui32Idx == psMenu->ui32FocusIndex)
+ {
+ //
+ // Set the foreground to the highlight color, and fill the
+ // rectangle of the background of this menu item.
+ //
+ GrContextForegroundSet(psContext, psMenuWidget->ui32ColorHighlight);
+ GrRectFill(psContext, &sRect);
+
+ //
+ // Set the new foreground to the normal foreground color, and
+ // set the background to the highlight color. This is so
+ // remaining drawing operations will have the correct background
+ // and foreground colors for this highlighted menu item cell.
+ //
+ GrContextForegroundSet(psContext, psMenuWidget->ui32ColorForeground);
+ GrContextBackgroundSet(psContext, psMenuWidget->ui32ColorHighlight);
+
+ //
+ // If this menu has a parent, then draw a left arrow icon on the
+ // focused menu item.
+ //
+ if(psMenu->psParent)
+ {
+ GrImageDraw(psContext, g_ui8LtArrow, sRect.i16XMin + 4,
+ sRect.i16YMin +
+ (psMenuWidget->ui32MenuItemHeight / 2) - 4);
+ }
+
+ //
+ // If this menu has a child menu or child widget, then draw a
+ // right arrow icon on the focused menu item.
+ //
+ if(psMenu->psSlideMenuItems[ui32Idx].psChildMenu ||
+ psMenu->psSlideMenuItems[ui32Idx].psChildWidget)
+ {
+ GrImageDraw(psContext, g_ui8RtArrow, sRect.i16XMax - 8,
+ sRect.i16YMin +
+ (psMenuWidget->ui32MenuItemHeight / 2) - 4);
+ }
+ }
+
+ //
+ // Otherwise this is a normal, non-highlighted menu item cell,
+ // so set the normal background color.
+ //
+ else
+ {
+ GrContextBackgroundSet(psContext, psMenuWidget->ui32ColorBackground);
+ }
+
+ //
+ // If the current menu is multi-selectable, then draw a checkbox on
+ // the menu item. Draw a checked or unchecked box depending on whether
+ // the item has been selected.
+ //
+ if(psMenu->bMultiSelectable)
+ {
+ if(psMenu->ui32SelectedFlags & (1 << ui32Idx))
+ {
+ GrImageDraw(psContext, g_ui8Checked, sRect.i16XMax - 12,
+ sRect.i16YMin +
+ (psMenuWidget->ui32MenuItemHeight / 2) - 4);
+ }
+ else
+ {
+ GrImageDraw(psContext, g_ui8Unchecked, sRect.i16XMax - 12,
+ sRect.i16YMin +
+ (psMenuWidget->ui32MenuItemHeight / 2) - 4);
+ }
+
+ }
+
+ //
+ // Draw the rectangle representing the menu item
+ //
+ GrRectDraw(psContext, &sRect);
+
+ //
+ // Draw the text for this menu item in the middle of the menu item
+ // rectangle (cell).
+ //
+ GrStringDrawCentered(psContext,
+ psMenu->psSlideMenuItems[ui32Idx].pcText,
+ -1,
+ psMenuWidget->sBase.psDisplay->ui16Width / 2,
+ sRect.i16YMin + \
+ (psMenuWidget->ui32MenuItemHeight / 2) - 1, 0);
+
+ //
+ // Advance to the next menu item, and update the menu item rectangle
+ // bounds to the next position
+ //
+ ui32Idx++;
+ sRect.i16YMin += psMenuWidget->ui32MenuItemHeight;
+ sRect.i16YMax += psMenuWidget->ui32MenuItemHeight;
+
+ //
+ // Note that this may attempt to render menu items that run off the
+ // bottom of the drawing area, but these will just be clipped and a
+ // little bit of processing time is wasted.
+ //
+ }
+}
+
+//*****************************************************************************
+//
+//! Paints a menu, menu items on a display.
+//!
+//! \param psWidget is a pointer to the slide menu widget to be drawn.
+//!
+//! This function draws the contents of a slide menu on the display. This is
+//! called in response to a \b WIDGET_MSG_PAINT message.
+//!
+//! \return None.
+//
+//*****************************************************************************
+static void
+SlideMenuPaint(tWidget *psWidget)
+{
+ tSlideMenuWidget *psMenuWidget;
+ tContext sContext;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(psWidget);
+
+ //
+ // If this widget has a child widget, that means that the menu has
+ // slid off the screen and the child widget is in control. Therefore
+ // there is nothing to paint here. Just exit and the child widget will
+ // be painted.
+ //
+ if(psWidget->psChild)
+ {
+ return;
+ }
+
+ //
+ // Convert the generic widget pointer into a slide menu widget pointer,
+ // and get a pointer to its context.
+ //
+ psMenuWidget = (tSlideMenuWidget *)psWidget;
+
+ //
+ // Initialize a context for the primary off-screen drawing buffer.
+ // Clip region is set to entire display by default, which is what we want.
+ //
+ GrContextInit(&sContext, psMenuWidget->psDisplayA);
+
+ //
+ // Render the menu into the off-screen buffer, using normal vertical
+ // position.
+ //
+ SlideMenuDraw(psMenuWidget, &sContext, 0);
+
+ //
+ // Initialize a drawing context for the display where the widget is to be
+ // drawn. This is the physical display, not an off-screen buffer.
+ //
+ GrContextInit(&sContext, psWidget->psDisplay);
+
+ //
+ // Initialize the clipping region on the physical display, based on the
+ // extents of this widget.
+ //
+ GrContextClipRegionSet(&sContext, &(psWidget->sPosition));
+
+ //
+ // Now copy the rendered menu into the physical display. This will show
+ // the menu on the display.
+ //
+ GrImageDraw(&sContext, psMenuWidget->psDisplayA->pvDisplayData,
+ psWidget->sPosition.i16XMin, psWidget->sPosition.i16YMin);
+}
+
+//*****************************************************************************
+//
+//! Performs the sliding menu operation, in response to the "down" button.
+//!
+//! \param psWidget is a pointer to the slide menu widget to move down.
+//!
+//! This function will respond to the "down" key/button event. The down
+//! button is used to select the next menu item down the list, and the effect
+//! is that the menu itself slides up, leaving the highlighted menu item
+//! in the middle of the screen.
+//!
+//! This function repeatedly draws the menu onto the display until the sliding
+//! animation is finished and will not return to the caller until then. This
+//! function is usually called from the thread context of
+//! WidgetMessageQueueProcess().
+//!
+//! \return Returns a non-zero value if the menu was moved or was not moved
+//! because it is already at the last position. If a child widget is active
+//! then this function does nothing and returns a 0.
+//
+//*****************************************************************************
+static int32_t
+SlideMenuDown(tWidget *psWidget)
+{
+ tSlideMenuWidget *psMenuWidget;
+ tSlideMenu *psMenu;
+ tContext sContext;
+ uint32_t ui32MenuHeight;
+ uint32_t ui32Y;
+
+ //
+ // If this menu widget has a child widget, that means the child widget
+ // is in control of the display, and there is nothing to do here.
+ //
+ if(psWidget->psChild)
+ {
+ return(0);
+ }
+
+ //
+ // Get handy pointers to the menu widget, and the menu that is currently
+ // displayed.
+ //
+ psMenuWidget = (tSlideMenuWidget *)psWidget;
+ psMenu = psMenuWidget->psSlideMenu;
+
+ //
+ // If we are already at the end of the list of menu items, then there
+ // is nothing else to do.
+ //
+ if(psMenu->ui32FocusIndex >= (psMenu->ui32Items - 1))
+ {
+ return(1);
+ }
+
+ //
+ // Increment focus menu item. This has the effect of selecting the next
+ // menu item in the list.
+ //
+ psMenu->ui32FocusIndex++;
+
+ //
+ // Initialize a context for the primary off-screen drawing buffer.
+ // Clip region is set to entire display by default, which is what we want.
+ //
+ GrContextInit(&sContext, psMenuWidget->psDisplayA);
+
+ //
+ // Render the menu into the off-screen buffer. This will be the same
+ // menu appearance as before, except the highlighted item has changed
+ // to the next menu item down.
+ //
+ SlideMenuDraw(psMenuWidget, &sContext, 0);
+
+ //
+ // Draw a continuation of this menu in the second offscreen buffer.
+ // This is the part of the menu that would be drawn if the display were
+ // twice as tall. We are effectively creating a virtual display that is
+ // twice as tall as the physical display.
+ //
+ GrContextInit(&sContext, psMenuWidget->psDisplayB);
+ SlideMenuDraw(psMenuWidget, &sContext, -1 *
+ (psMenuWidget->sBase.sPosition.i16YMax -
+ psMenuWidget->sBase.sPosition.i16YMin));
+
+ //
+ // Initialize a drawing context for the display where the widget is to be
+ // drawn. This is the physical display, not an off-screen buffer.
+ //
+ GrContextInit(&sContext, psWidget->psDisplay);
+
+ //
+ // Initialize the clipping region on the physical display, based on the
+ // extents of this widget.
+ //
+ GrContextClipRegionSet(&sContext, &(psWidget->sPosition));
+
+ //
+ // Get the height of the displayed part of the menu.
+ //
+ ui32MenuHeight = psMenuWidget->psDisplayA->ui16Height;
+
+ //
+ // Now copy the rendered menu into the physical display
+ //
+ // Iterate over the Y displacement of one menu item cell. This loop
+ // will repeatedly draw both off screen buffers to the physical display,
+ // adjusting the position of each by one pixel each time it is drawn. Each
+ // time the offset is changed so that both buffers are drawn one higher
+ // than the previous time. This will have the effect of "sliding" the
+ // entire menu up by the height of one menu item cell.
+ // The speed of the animation is controlled entirely by the speed of the
+ // processor and the speed of the interface to the physical display.
+ //
+ for(ui32Y = 0; ui32Y <= psMenuWidget->ui32MenuItemHeight; ui32Y++)
+ {
+ GrImageDraw(&sContext, psMenuWidget->psDisplayA->pvDisplayData,
+ psWidget->sPosition.i16XMin,
+ psWidget->sPosition.i16YMin - ui32Y);
+ GrImageDraw(&sContext, psMenuWidget->psDisplayB->pvDisplayData,
+ psWidget->sPosition.i16XMin,
+ psWidget->sPosition.i16YMin + ui32MenuHeight - ui32Y);
+ }
+
+ //
+ // Increment centered menu item. This will now match the menu item with
+ // the focus. When the menu is repainted again, the newly selected
+ // menu item will be centered and highlighted.
+ //
+ psMenu->ui32CenterIndex = psMenu->ui32FocusIndex;
+
+ //
+ // Initialize a context for the primary off-screen drawing buffer.
+ // Clip region is set to entire display by default, which is what we want.
+ //
+ GrContextInit(&sContext, psMenuWidget->psDisplayA);
+
+ //
+ // Render the menu into the off-screen buffer. This will be the same
+ // menu appearance as before, except the highlighted item has changed
+ // to the next menu item down. Now when a repaint occurs the menu
+ // will be redrawn with the newly highlighted menu item.
+ //
+ SlideMenuDraw(psMenuWidget, &sContext, 0);
+
+ //
+ // Return indication that we handled the key event.
+ //
+ return(1);
+}
+
+//*****************************************************************************
+//
+//! Performs the sliding menu operation, in response to the "up" button.
+//!
+//! \param psWidget is a pointer to the slide menu widget to move up.
+//!
+//! This function will respond to the "up" key/button event. The up
+//! button is used to select the previous menu item down the list, and the
+//! effect is that the menu itself slides down, leaving the highlighted menu
+//! item in the middle of the screen.
+//!
+//! This function repeatedly draws the menu onto the display until the sliding
+//! animation is finished and will not return to the caller until then. This
+//! function is usually called from the thread context of
+//! WidgetMessageQueueProcess().
+//!
+//! \return Returns a non-zero value if the menu was moved or was not moved
+//! because it is already at the first position. If a child widget is active
+//! then this function does nothing and returns a 0.
+//
+//*****************************************************************************
+static int32_t
+SlideMenuUp(tWidget *psWidget)
+{
+ tSlideMenuWidget *psMenuWidget;
+ tSlideMenu *psMenu;
+ tContext sContext;
+ uint32_t ui32MenuHeight;
+ uint32_t ui32Y;
+
+ //
+ // If this menu widget has a child widget, that means the child widget
+ // is in control of the display, and there is nothing to do here.
+ //
+ if(psWidget->psChild)
+ {
+ return(0);
+ }
+
+ //
+ // Get handy pointers to the menu widget, and the menu that is currently
+ // displayed.
+ //
+ psMenuWidget = (tSlideMenuWidget *)psWidget;
+ psMenu = psMenuWidget->psSlideMenu;
+
+ //
+ // If we are already at the start of the list of menu items, then there
+ // is nothing else to do.
+ //
+ if(psMenu->ui32FocusIndex == 0)
+ {
+ return(1);
+ }
+
+ //
+ // Decrement the focus menu item. This has the effect of selecting the
+ // previous menu item in the list.
+ //
+ psMenu->ui32FocusIndex--;
+
+ //
+ // Initialize a context for the primary off-screen drawing buffer.
+ // Clip region is set to entire display by default, which is what we want.
+ //
+ GrContextInit(&sContext, psMenuWidget->psDisplayA);
+
+ //
+ // Render the menu into the off-screen buffer. This will be the same
+ // menu appearance as before, except the highlighted item has changed
+ // to the previous menu item up.
+ //
+ SlideMenuDraw(psMenuWidget, &sContext, 0);
+
+ //
+ // Draw a continuation of this menu in the second offscreen buffer.
+ // This is the part of the menu that would be drawn above this menu if the
+ // display were twice as tall. We are effectively creating a virtual
+ // display that is twice as tall as the physical display.
+ //
+ GrContextInit(&sContext, psMenuWidget->psDisplayB);
+ SlideMenuDraw(psMenuWidget, &sContext,
+ (psMenuWidget->sBase.sPosition.i16YMax -
+ psMenuWidget->sBase.sPosition.i16YMin));
+
+ //
+ // Initialize a drawing context for the display where the widget is to be
+ // drawn. This is the physical display, not an off-screen buffer.
+ //
+ GrContextInit(&sContext, psWidget->psDisplay);
+
+ //
+ // Initialize the clipping region on the physical display, based on the
+ // extents of this widget.
+ //
+ GrContextClipRegionSet(&sContext, &(psWidget->sPosition));
+
+ //
+ // Get the height of the displayed part of the menu.
+ //
+ ui32MenuHeight = psMenuWidget->psDisplayA->ui16Height;
+
+ //
+ // Now copy the rendered menu into the physical display
+ //
+ // Iterate over the Y displacement of one menu item cell. This loop
+ // will repeatedly draw both off screen buffers to the physical display,
+ // adjusting the position of each by one pixel each time it is drawn. Each
+ // time the offset is changed so that both buffers are drawn one lower
+ // than the previous time. This will have the effect of "sliding" the
+ // entire menu down by the height of one menu item cell.
+ // The speed of the animation is controlled entirely by the speed of the
+ // processor and the speed of the interface to the physical display.
+ //
+ for(ui32Y = 0; ui32Y <= psMenuWidget->ui32MenuItemHeight; ui32Y++)
+ {
+ GrImageDraw(&sContext, psMenuWidget->psDisplayB->pvDisplayData,
+ psWidget->sPosition.i16XMin,
+ psWidget->sPosition.i16YMin + ui32Y - ui32MenuHeight);
+ GrImageDraw(&sContext, psMenuWidget->psDisplayA->pvDisplayData,
+ psWidget->sPosition.i16XMin,
+ psWidget->sPosition.i16YMin + ui32Y);
+ }
+
+ //
+ // Decrement the centered menu item. This will now match the menu item
+ // with the focus. When the menu is repainted again, the newly selected
+ // menu item will be centered and highlighted.
+ //
+ psMenu->ui32CenterIndex = psMenu->ui32FocusIndex;
+
+ //
+ // Initialize a context for the primary off-screen drawing buffer.
+ // Clip region is set to entire display by default, which is what we want.
+ //
+ GrContextInit(&sContext, psMenuWidget->psDisplayA);
+
+ //
+ // Render the menu into the off-screen buffer. This will be the same
+ // menu appearance as before, except the highlighted item has changed
+ // to the next menu item up. Now when a repaint occurs the menu
+ // will be redrawn with the newly highlighted menu item.
+ //
+ SlideMenuDraw(psMenuWidget, &sContext, 0);
+
+ //
+ // Return indication that we handled the key event.
+ //
+ return(1);
+}
+
+//*****************************************************************************
+//
+//! Performs the sliding menu operation, in response to the "right" button.
+//!
+//! \param psWidget is a pointer to the slide menu widget to move to the right.
+//!
+//! This function will respond to the "right" key/button event. The right
+//! button is used to select the next menu level below the current menu item,
+//! or a widget that is activated by the menu item. The effect is that the
+//! menu itself slides off to the left, and the new menu or widget slides in
+//! from the right.
+//!
+//! This function repeatedly draws the menu onto the display until the sliding
+//! animation is finished and will not return to the caller until then. This
+//! function is usually called from the thread context of
+//! WidgetMessageQueueProcess().
+//!
+//! \return Returns a non-zero value if the menu was moved or was not moved
+//! because it is already at the last position. If a child widget is active
+//! then this function does nothing and returns a 0.
+//
+//*****************************************************************************
+static int32_t
+SlideMenuRight(tWidget *psWidget)
+{
+ tSlideMenuWidget *psMenuWidget;
+ tSlideMenu *psMenu;
+ tSlideMenu *psChildMenu;
+ tContext sContext;
+ tWidget *psChildWidget;
+ uint32_t ui32X;
+ uint32_t ui32MenuWidth;
+
+ //
+ // If this menu widget has a child widget, that means the child widget
+ // is in control of the display, and there is nothing to do here.
+ //
+ if(psWidget->psChild)
+ {
+ return(0);
+ }
+
+ //
+ // Get handy pointers to the menu widget, and the current menu, and the
+ // child menu and widget if they exist.
+ //
+ psMenuWidget = (tSlideMenuWidget *)psWidget;
+ psMenu = psMenuWidget->psSlideMenu;
+ psChildMenu = psMenu->psSlideMenuItems[psMenu->ui32FocusIndex].psChildMenu;
+ psChildWidget = psMenu->psSlideMenuItems[psMenu->ui32FocusIndex].psChildWidget;
+
+ //
+ // Initialize a context for the secondary off-screen drawing buffer.
+ // Clip region is set to entire display by default, which is what we want.
+ //
+ GrContextInit(&sContext, psMenuWidget->psDisplayB);
+
+ //
+ // Render the current menu into off-screen buffer B. This
+ // will be the same menu appearance as is already being shown.
+ //
+ SlideMenuDraw(psMenuWidget, &sContext, 0);
+
+ //
+ // Now set up context for drawing into off-screen buffer A
+ //
+ GrContextInit(&sContext, psMenuWidget->psDisplayA);
+
+ //
+ // Process child menu of this menu item
+ //
+ if(psChildMenu)
+ {
+ //
+ // Switch the active menu for this SlideMenuWidget to be the child
+ // menu
+ //
+ psMenuWidget->psSlideMenu = psChildMenu;
+
+ //
+ // Draw the new (child) menu into off-screen buffer A
+ //
+ SlideMenuDraw(psMenuWidget, &sContext, 0);
+ }
+
+ //
+ // Process child widget of this menu item. This only happens if there
+ // is no child menu.
+ //
+ else if(psChildWidget)
+ {
+ //
+ // Call the widget activated callback function. This will notify
+ // the application that a child widget has been activated by the
+ // menu system.
+ //
+ if(psMenuWidget->pfnActive)
+ {
+ psMenuWidget->pfnActive(psChildWidget,
+ &psMenu->psSlideMenuItems[psMenu->ui32FocusIndex],
+ 1);
+ }
+
+ //
+ // Link the new child widget into this SlideMenuWidget so
+ // it appears as a child to this widget. Normally the menu widget
+ // has no child widget.
+ //
+ psWidget->psChild = psChildWidget;
+ psChildWidget->psParent = psWidget;
+
+ //
+ // Fill a rectangle with the new child widget background color.
+ // This is done in off-screen buffer A. When the menu slides off,
+ // it will be replaced by a blank background that will then be
+ // controlled by the new child widget.
+ //
+ GrContextForegroundSet(
+ &sContext,
+ psMenu->psSlideMenuItems[psMenu->ui32FocusIndex].ui32ChildWidgetColor);
+ GrRectFill(&sContext, &sContext.sClipRegion);
+
+ //
+ // Request a repaint for the child widget so it can draw itself once
+ // the menu slide is done.
+ //
+ WidgetPaint(psChildWidget);
+ }
+
+ //
+ // There is no child menu or child widget, so there is nothing to change
+ // on the display.
+ //
+ else
+ {
+ return(1);
+ }
+
+ //
+ // Initialize a drawing context for the display where the widget is to be
+ // drawn. This is the physical display, not an off-screen buffer.
+ //
+ GrContextInit(&sContext, psWidget->psDisplay);
+
+ //
+ // Initialize the clipping region on the physical display, based on the
+ // extents of this widget.
+ //
+ GrContextClipRegionSet(&sContext, &(psWidget->sPosition));
+
+ //
+ // Get the width of the menu widget which is used in calculations below
+ //
+ ui32MenuWidth = psMenuWidget->psDisplayA->ui16Width;
+
+ //
+ // The following loop draws the two off-screen buffers onto the physical
+ // display using a right-to-left-wipe. This will provide an appearance
+ // of sliding to the left. The new child menu, or child widget background
+ // will slide in from the right. The "old" menu is being held in
+ // off-screen buffer B and the new one is in buffer A. So when we are
+ // done, the correct image will be in buffer A.
+ //
+ for(ui32X = 0; ui32X <= ui32MenuWidth; ui32X += 8)
+ {
+ GrImageDraw(&sContext, psMenuWidget->psDisplayB->pvDisplayData,
+ psWidget->sPosition.i16XMin - ui32X,
+ psWidget->sPosition.i16YMin);
+ GrImageDraw(&sContext, psMenuWidget->psDisplayA->pvDisplayData,
+ psWidget->sPosition.i16XMin + ui32MenuWidth - ui32X,
+ psWidget->sPosition.i16YMin);
+ }
+
+ //
+ // Return indication that we handled the key event.
+ //
+ return(1);
+}
+
+//*****************************************************************************
+//
+//! Performs the sliding menu operation, in response to the "left" button.
+//!
+//! \param psWidget is a pointer to the slide menu widget to move to the left.
+//!
+//! This function will respond to the "left" key/button event. The left
+//! button is used to ascend to the next menu up in the menu tree. The effect
+//! is that the current menu, or active widget, slides off to the right, while
+//! the parent menu slides in from the left.
+//!
+//! This function repeatedly draws the menu onto the display until the sliding
+//! animation is finished and will not return to the caller until then. This
+//! function is usually called from the thread context of
+//! WidgetMessageQueueProcess().
+//!
+//! \return Returns a non-zero value if the menu was moved or was not moved
+//! because it is already at the last position. If a child widget is active
+//! then this function does nothing and returns a 0.
+//
+//*****************************************************************************
+static int32_t
+SlideMenuLeft(tWidget *psWidget)
+{
+ tSlideMenuWidget *psMenuWidget;
+ tSlideMenu *psMenu;
+ tSlideMenu *psParentMenu;
+ tContext sContext;
+ uint32_t ui32X;
+ uint32_t ui32MenuWidth;
+
+ //
+ // Get handy pointers to the menu widget and active menu, and the parent
+ // menu if there is one.
+ //
+ psMenuWidget = (tSlideMenuWidget *)psWidget;
+ psMenu = psMenuWidget->psSlideMenu;
+ psParentMenu = psMenu->psParent;
+
+ //
+ // Initialize a context for the primary off-screen drawing buffer.
+ // Clip region is set to entire display by default, which is what we want.
+ //
+ GrContextInit(&sContext, psMenuWidget->psDisplayB);
+
+ //
+ // If this widget has a child, that means that the child widget is in
+ // control, and we are requested to go back to the previous menu item.
+ // Process the child widget.
+ //
+ if(psWidget->psChild)
+ {
+ //
+ // Call the widget de-activated callback function. This notifies the
+ // application that the widget is being deactivated.
+ //
+ if(psMenuWidget->pfnActive)
+ {
+ psMenuWidget->pfnActive(psWidget->psChild,
+ &psMenu->psSlideMenuItems[psMenu->ui32FocusIndex],
+ 0);
+ }
+
+ //
+ // Unlink the child widget from the slide menu widget. The menu
+ // widget will now no longer have a child widget.
+ //
+ psWidget->psChild->psParent = 0;
+ psWidget->psChild = 0;
+
+ //
+ // Fill a rectangle with the child widget background color. This will
+ // erase everything else that is shown on the widget but leave the
+ // background, which will make the change visually less jarring.
+ // This is done in off-screen buffer B, which is the buffer that is
+ // going to be slid off the screen.
+ //
+ GrContextForegroundSet(
+ &sContext,
+ psMenu->psSlideMenuItems[psMenu->ui32FocusIndex].ui32ChildWidgetColor);
+ GrRectFill(&sContext, &sContext.sClipRegion);
+ }
+
+ //
+ // Otherwise there is not a child widget in control, so process the parent
+ // menu, if there is one.
+ //
+ else if(psParentMenu)
+ {
+ //
+ // Render the current menu into the off-screen buffer B. This will be
+ // the same menu appearance that is currently on the display.
+ //
+ SlideMenuDraw(psMenuWidget, &sContext, 0);
+
+ //
+ // Now switch the widget to the parent menu
+ //
+ psMenuWidget->psSlideMenu = psParentMenu;
+ }
+
+ //
+ // Otherwise, we are already at the top level menu and there is nothing
+ // else to do.
+ //
+ else
+ {
+ return(1);
+ }
+
+ //
+ // Draw the new menu in the second offscreen buffer. This is the menu
+ // that will be on the display when the animation is over.
+ //
+ GrContextInit(&sContext, psMenuWidget->psDisplayA);
+ SlideMenuDraw(psMenuWidget, &sContext, 0);
+
+ //
+ // Initialize a drawing context for the display where the widget is to be
+ // drawn. This is the physical display, not an off-screen buffer.
+ //
+ GrContextInit(&sContext, psWidget->psDisplay);
+
+ //
+ // Initialize the clipping region on the physical display, based on the
+ // extents of this widget.
+ //
+ GrContextClipRegionSet(&sContext, &(psWidget->sPosition));
+
+ //
+ // Get the width of the menu widget.
+ //
+ ui32MenuWidth = psMenuWidget->psDisplayA->ui16Width;
+
+ //
+ // The following loop draws the two off-screen buffers onto the physical
+ // display using a left-to-right. This will provide an appearance
+ // of sliding to the right. The parent menu will slide in from the left.
+ // The "old" child menu is being held in off-screen buffer B and the new
+ // one is in buffer A. So when we are done, the correct image will be in
+ // buffer A.
+ //
+ for(ui32X = 0; ui32X <= ui32MenuWidth; ui32X += 8)
+ {
+ GrImageDraw(&sContext, psMenuWidget->psDisplayB->pvDisplayData,
+ psWidget->sPosition.i16XMin + ui32X,
+ psWidget->sPosition.i16YMin);
+ GrImageDraw(&sContext, psMenuWidget->psDisplayA->pvDisplayData,
+ psWidget->sPosition.i16XMin + ui32X - ui32MenuWidth,
+ psWidget->sPosition.i16YMin);
+ }
+
+ //
+ // Return indication that we handled the key event.
+ //
+ return(1);
+}
+
+//*****************************************************************************
+//
+//! Handles menu selection, in response to the "select" button.
+//!
+//! \param psWidget is a pointer to the slide menu widget to use for a
+//! select operation.
+//!
+//! This function will allow for checking or unchecking multi-selectable
+//! menu items. If the menu does not allow multiple selection, then it
+//! treats it as a "right" button press.
+//!
+//! \return Returns a non-zero value if the key was handled. Returns 0 if the
+//! key was not handled.
+//
+//*****************************************************************************
+static int32_t
+SlideMenuClick(tWidget *psWidget)
+{
+ tSlideMenuWidget *psMenuWidget;
+ tSlideMenu *psMenu;
+
+ //
+ // If a child widget is in control then there is nothing to do.
+ //
+ if(psWidget->psChild)
+ {
+ return(0);
+ }
+
+ //
+ // Get handy pointers to the menu widget and current menu.
+ //
+ psMenuWidget = (tSlideMenuWidget *)psWidget;
+ psMenu = psMenuWidget->psSlideMenu;
+
+ //
+ // Check to see if this menu allows multiple selection.
+ //
+ if(psMenu->bMultiSelectable)
+ {
+ //
+ // Toggle the selection status of the currently highlighted menu
+ // item, and then repaint it.
+ //
+ psMenu->ui32SelectedFlags ^= 1 << psMenu->ui32FocusIndex;
+ SlideMenuPaint(psWidget);
+
+ //
+ // We are done so return indication that we handled the key event.
+ //
+ return(1);
+ }
+
+ //
+ // Otherwise, treat the select button the same as a right button.
+ //
+ return(SlideMenuRight(psWidget));
+}
+
+//*****************************************************************************
+//
+//! Process key/button event to decide how to move the sliding menu.
+//!
+//! \param psWidget is a pointer to the slide menu widget to process.
+//! \param ui32Msg is the message containing the key event.
+//!
+//! This function is used to specifically handle key events destined for the
+//! slide menu widget. It decides which menu movement function should be
+//! called for each key event.
+//!
+//! \return Returns an indication if the key was handled. Non-zero if the
+//! key event was handled or else 0.
+//
+//*****************************************************************************
+static int32_t
+SlideMenuMove(tWidget *psWidget, uint32_t ui32Msg)
+{
+ //
+ // Process the key event.
+ //
+ switch(ui32Msg)
+ {
+ //
+ // User presses select button.
+ //
+ case WIDGET_MSG_KEY_SELECT:
+ {
+ return(SlideMenuClick(psWidget));
+ }
+
+ //
+ // User presses up button.
+ //
+ case WIDGET_MSG_KEY_UP:
+ {
+ return(SlideMenuUp(psWidget));
+ }
+
+ //
+ // User presses down button.
+ //
+ case WIDGET_MSG_KEY_DOWN:
+ {
+ return(SlideMenuDown(psWidget));
+ }
+
+ //
+ // User presses left button.
+ //
+ case WIDGET_MSG_KEY_LEFT:
+ {
+ return(SlideMenuLeft(psWidget));
+ }
+
+ //
+ // User presses right button.
+ //
+ case WIDGET_MSG_KEY_RIGHT:
+ {
+ return(SlideMenuRight(psWidget));
+ }
+
+ //
+ // This is an unexpected event. Return an indication that the event
+ // was not handled.
+ //
+ default:
+ {
+ return(0);
+ }
+ }
+}
+
+//*****************************************************************************
+//
+//! Handles messages for a slide menu widget.
+//!
+//! \param psWidget is a pointer to the slide menu widget.
+//! \param ui32Msg is the message.
+//! \param ui32Param1 is the first parameter to the message.
+//! \param ui32Param2 is the second parameter to the message.
+//!
+//! This function receives messages intended for this slide menu widget and
+//! processes them accordingly. The processing of the message varies based on
+//! the message in question.
+//!
+//! Unrecognized messages are handled by calling WidgetDefaultMsgProc().
+//!
+//! \return Returns a value appropriate to the supplied message.
+//
+//*****************************************************************************
+int32_t
+SlideMenuMsgProc(tWidget *psWidget, uint32_t ui32Msg, uint32_t ui32Param1,
+ uint32_t ui32Param2)
+{
+ //
+ // Check the arguments.
+ //
+ ASSERT(psWidget);
+
+ //
+ // Determine which message is being sent.
+ //
+ switch(ui32Msg)
+ {
+ //
+ // The widget paint request has been sent.
+ //
+ case WIDGET_MSG_PAINT:
+ {
+ //
+ // Handle the widget paint request.
+ //
+ SlideMenuPaint(psWidget);
+
+ //
+ // Return one to indicate that the message was successfully
+ // processed.
+ //
+ return(1);
+ }
+
+ //
+ // A key event has been received. By convention, this widget will
+ // process the key events if ui32Param1 is set to this widget.
+ // Otherwise a different widget has the "focus" for key events.
+ //
+ case WIDGET_MSG_KEY_SELECT:
+ case WIDGET_MSG_KEY_UP:
+ case WIDGET_MSG_KEY_DOWN:
+ case WIDGET_MSG_KEY_LEFT:
+ case WIDGET_MSG_KEY_RIGHT:
+ {
+ //
+ // If this key event is for us, then process the event.
+ //
+ if((tWidget *)ui32Param1 == psWidget)
+ {
+ return(SlideMenuMove(psWidget, ui32Msg));
+ }
+ }
+
+ //
+ // An unknown request has been sent. This widget does not handle
+ // pointer events, so they get dumped here if they occur.
+ //
+ default:
+ {
+ //
+ // Let the default message handler process this message.
+ //
+ return(WidgetDefaultMsgProc(psWidget, ui32Msg, ui32Param1,
+ ui32Param2));
+ }
+ }
+}
+
+//*****************************************************************************
+//
+//! Initializes a slide menu widget.
+//!
+//! \param psWidget is a pointer to the slide menu widget to initialize.
+//! \param psDisplay is a pointer to the display on which to draw the menu.
+//! \param i32X is the X coordinate of the upper left corner of the canvas.
+//! \param i32Y is the Y coordinate of the upper left corner of the canvas.
+//! \param i32Width is the width of the canvas.
+//! \param i32Height is the height of the canvas.
+//! \param psDisplayOffA is one of two off-screen displays used for rendering.
+//! \param psDisplayOffB is one of two off-screen displays used for rendering.
+//! \param ui32ItemHeight is the height of a menu item
+//! \param ui32Foreground is the foreground color used for menu item boundaries
+//! and text.
+//! \param ui32Background is the background color of a menu item.
+//! \param ui32Highlight is the color of a highlighted menu item.
+//! \param psFont is a pointer to the font that should be used for text.
+//! \param psMenu is the initial menu to display
+//!
+//! This function initializes the caller provided slide menu widget.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+SlideMenuInit(tSlideMenuWidget *psWidget, const tDisplay *psDisplay,
+ int32_t i32X, int32_t i32Y, int32_t i32Width, int32_t i32Height,
+ tDisplay *psDisplayOffA, tDisplay *psDisplayOffB,
+ uint32_t ui32ItemHeight, uint32_t ui32Foreground,
+ uint32_t ui32Background, uint32_t ui32Highlight,
+ tFont *psFont, tSlideMenu *psMenu)
+{
+ uint32_t ui32Idx;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(psWidget);
+ ASSERT(psDisplay);
+ ASSERT(psDisplayOffA);
+ ASSERT(psDisplayOffB);
+ ASSERT(psFont);
+ ASSERT(psMenu);
+
+ //
+ // Clear out the widget structure.
+ //
+ for(ui32Idx = 0; ui32Idx < sizeof(tSlideMenuWidget); ui32Idx += 4)
+ {
+ ((uint32_t *)psWidget)[ui32Idx / 4] = 0;
+ }
+
+ //
+ // Set the size of the widget structure.
+ //
+ psWidget->sBase.i32Size = sizeof(tSlideMenuWidget);
+
+ //
+ // Mark this widget as fully disconnected.
+ //
+ psWidget->sBase.psParent = 0;
+ psWidget->sBase.psNext = 0;
+ psWidget->sBase.psChild = 0;
+
+ //
+ // Save the display pointer.
+ //
+ psWidget->sBase.psDisplay = psDisplay;
+
+ //
+ // Set the extents of the display area.
+ //
+ psWidget->sBase.sPosition.i16XMin = i32X;
+ psWidget->sBase.sPosition.i16YMin = i32Y;
+ psWidget->sBase.sPosition.i16XMax = i32X + i32Width - 1;
+ psWidget->sBase.sPosition.i16YMax = i32Y + i32Height - 1;
+
+ //
+ // Initialize the widget fields
+ //
+ psWidget->psDisplayA = psDisplayOffA;
+ psWidget->psDisplayB = psDisplayOffB;
+ psWidget->ui32MenuItemHeight = ui32ItemHeight;
+ psWidget->ui32ColorForeground = ui32Foreground;
+ psWidget->ui32ColorBackground = ui32Background;
+ psWidget->ui32ColorHighlight = ui32Highlight;
+ psWidget->psFont = psFont;
+ psWidget->psSlideMenu = psMenu;
+
+ //
+ // Use the slide menu message handler to process messages to this widget.
+ //
+ psWidget->sBase.pfnMsgProc = SlideMenuMsgProc;
+}
+
+//*****************************************************************************
+//
+// Close the Doxygen group.
+//! @}
+//
+//*****************************************************************************