From c3e4c9a25c2910d2d66d52215b3406b13d5b23d5 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Sun, 29 Jun 2014 12:34:32 +0300 Subject: Add more board models --- boards/ek-lm4f232/drivers/slidemenuwidget.c | 1424 +++++++++++++++++++++++++++ 1 file changed, 1424 insertions(+) create mode 100644 boards/ek-lm4f232/drivers/slidemenuwidget.c (limited to 'boards/ek-lm4f232/drivers/slidemenuwidget.c') 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 +#include +#include +#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. +//! @} +// +//***************************************************************************** -- cgit v1.3.1