/**************************************************************************** * NxWidgets/libnxwidgets/src/cnxwidget.cxx * * Copyright (C) 2012 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX, NxWidgets, nor the names of its contributors * me be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * **************************************************************************** * * Portions of this package derive from Woopsi (http://woopsi.org/) and * portions are original efforts. It is difficult to determine at this * point what parts are original efforts and which parts derive from Woopsi. * However, in any event, the work of Antony Dzeryn will be acknowledged * in most NxWidget files. Thanks Antony! * * Copyright (c) 2007-2011, Antony Dzeryn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the names "Woopsi", "Simian Zombie" nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Antony Dzeryn ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL Antony Dzeryn BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include "cnxwidget.hxx" #include "cgraphicsport.hxx" #include "cwidgeteventhandler.hxx" #include "cnxfont.hxx" #include "cwidgetstyle.hxx" #include "cwidgeteventargs.hxx" #include "cwidgetcontrol.hxx" #include "singletons.hxx" /**************************************************************************** * Pre-Processor Definitions ****************************************************************************/ #define DOUBLE_CLICK_BOUNDS 10 /**************************************************************************** * Method Implementations ****************************************************************************/ using namespace NXWidgets; /** * Constructor. * * @param pWidgetControl The controlling widget for the display * @param x The x coordinate of the widget. * @param y The y coordinate of the widget. * @param width The width of the widget. * @param height The height of the widget. * @param flags Bitmask specifying some set-up values for the widget. * @param style The style that the button should use. If this is not * specified, the button will use the global default widget * style. * @see WidgetFlagType. */ CNxWidget::CNxWidget(CWidgetControl *pWidgetControl, nxgl_coord_t x, nxgl_coord_t y, nxgl_coord_t width, nxgl_coord_t height, uint32_t flags, const CWidgetStyle *style) { // Save the controlling widget m_widgetControl = pWidgetControl; // Set properties from parameters. If this is a child widget, then // this coordinates are relative to the parent widget. m_rect.setX(x); m_rect.setY(y); m_rect.setWidth(width); m_rect.setHeight(height); // Do we need to fetch the default style? if (style == (CWidgetStyle *)NULL) { // Get the style from the controlling widget. This allows different // widgets within a window to have the same style, unique to the window. pWidgetControl->getWidgetStyle(&m_style); } else { // Use specified style useWidgetStyle(style); } // Add ourself to the list of controlled widgets pWidgetControl->addControlledWidget(this); // Mask flags against bitmasks and logical NOT twice to obtain boolean values m_flags.borderless = (!(!(flags & WIDGET_BORDERLESS))); m_flags.draggable = (!(!(flags & WIDGET_DRAGGABLE))); m_flags.permeable = (!(!(flags & WIDGET_PERMEABLE))); m_flags.doubleClickable = (!(!(flags & WIDGET_DOUBLE_CLICKABLE))); // Dragging values m_grabPointX = 0; m_grabPointY = 0; m_newX = 0; m_newY = 0; // Set initial flag values m_flags.clicked = false; m_flags.dragging = false; m_flags.hasFocus = false; m_flags.deleted = false; m_flags.drawingEnabled = false; m_flags.enabled = true; m_flags.erased = true; m_flags.hidden = false; // Set hierarchy pointers m_parent = (CNxWidget *)NULL; m_focusedChild = (CNxWidget *)NULL; // Double-click clock_gettime(CLOCK_REALTIME, &m_lastClickTime); m_lastClickX = 0; m_lastClickY = 0; m_doubleClickBounds = DOUBLE_CLICK_BOUNDS; // Set border size to 1 line m_borderSize.top = 1; m_borderSize.right = 1; m_borderSize.bottom = 1; m_borderSize.left = 1; m_widgetEventHandlers = new CWidgetEventHandlerList(this); } /** * Destructor. */ CNxWidget::~CNxWidget(void) { if (!m_flags.deleted) { m_flags.deleted = true; // Unset the clicked pointer if necessary if (m_widgetControl->getClickedWidget() == this) { m_widgetControl->setClickedWidget((CNxWidget *)NULL); } } if (m_parent != (CNxWidget *)NULL) { m_parent->removeChild(this); } // Delete children while (m_children.size() > 0) { m_children[0]->destroy(); } // Remove ourselve from the controlled widget list m_widgetControl->removeControlledWidget(this); // Delete instances. NOTE that we do not delete the controlling // widget. It persists until the window is closed. delete m_widgetEventHandlers; } /** * Get the x coordinate of the widget in "Widget space". * * @return Widget space x coordinate. */ nxgl_coord_t CNxWidget::getX(void) const { if (m_parent != (CNxWidget *)NULL) { return m_parent->getX() + m_rect.getX(); } return m_rect.getX(); } /** * Get the y coordinate of the widget in "Widget space". * * @return Widget space y coordinate. */ nxgl_coord_t CNxWidget::getY(void) const { if (m_parent != (CNxWidget *)NULL) { return m_parent->getY() + m_rect.getY(); } return m_rect.getY(); } /** * Get the x coordinate of the widget relative to its parent. * * @return Parent-space x coordinate. */ nxgl_coord_t CNxWidget::getRelativeX(void) const { return m_rect.getX(); } /** * Get the y coordinate of the widget relative to its parent. * * @return Parent-space y coordinate. */ nxgl_coord_t CNxWidget::getRelativeY(void) const { return m_rect.getY(); } /** * Has the widget been marked for deletion? This function recurses up the widget * hierarchy and only returns true if all of the widgets in the ancestor * chain are not deleted. * * Widgets marked for deletion are automatically deleted and should not be * interacted with. * * @return True if marked for deletion. */ bool CNxWidget::isDeleted(void) const { if (m_parent != (CNxWidget *)NULL) { if (m_parent->isDeleted()) { return true; } } return m_flags.deleted; } /** * Is the widget allowed to draw? This function recurses up the widget * hierarchy and only returns true if all of the widgets in the ancestor * chain are visible. * * @return True if drawing is enabled. */ bool CNxWidget::isDrawingEnabled(void) const { if (m_parent != (CNxWidget *)NULL) { if (m_parent->isDrawingEnabled()) { // Drawing is enabled if the widget is drawable, not deleted, and not hidden return (m_flags.drawingEnabled && !m_flags.deleted && !m_flags.hidden); } } else { // Drawing is enabled if the widget is drawable, not deleted, and not hidden return (m_flags.drawingEnabled && !m_flags.deleted && !m_flags.hidden); } return false; } /** * Is the widget hidden? This function recurses up the widget * hierarchy and returns true if any of the widgets in the ancestor * chain are hidden. * * @return True if hidden. */ bool CNxWidget::isHidden(void) const { if (m_parent != (CNxWidget *)NULL) { if (!m_parent->isHidden()) { // Hidden if the widget is deleted or hidden return (m_flags.deleted || m_flags.hidden); } } else { return (m_flags.deleted || m_flags.hidden); } return true; } /** * Is the widget enabled? This function recurses up the widget * hierarchy and only returns true if all of the widgets in the ancestor * chain are enabled. * * @return True if enabled. */ bool CNxWidget::isEnabled() const { if (m_parent != (CNxWidget *)NULL) { if (m_parent->isEnabled()) { // Enabled if the widget is enabled, not deleted and not hidden return (m_flags.enabled && (!m_flags.deleted) && (!m_flags.hidden)); } } else { return (m_flags.enabled && (!m_flags.deleted) && (!m_flags.hidden)); } return false; } /** * Insert the dimensions that this widget wants to have into the rect * passed in as a parameter. All coordinates are relative to the widget's * parent. * * @param rect Reference to a rect to populate with data. */ void CNxWidget::getPreferredDimensions(CRect &rect) const { rect = m_rect; } /** * Insert the properties of the space within this widget that is available * for children into the rect passed in as a parameter. * All coordinates are relative to this widget. * * @param rect Reference to a rect to populate with data. */ void CNxWidget::getClientRect(CRect &rect) const { if (m_flags.borderless) { rect.setX(0); rect.setY(0); rect.setWidth(getWidth()); rect.setHeight(getHeight()); } else { rect.setX(m_borderSize.left); rect.setY(m_borderSize.top); rect.setWidth(getWidth() - (m_borderSize.left + m_borderSize.right)); rect.setHeight(getHeight() - (m_borderSize.top + m_borderSize.bottom)); } } /** * Insert the properties of the space within this widget that is * available for children into the rect passed in as a parameter. * Identical to getClientRect() except that all coordinates are * absolute positions within the window. * * @param rect Reference to a rect to populate with data. */ void CNxWidget::getRect(CRect &rect) const { getClientRect(rect); rect.setX(rect.getX() + getX()); rect.setY(rect.getY() + getY()); } /** * Sets this widget's border state. * * @param isBorderless The border state. */ void CNxWidget::setBorderless(bool borderless) { m_flags.borderless = borderless; } /** * Sets the font. * * @param font A pointer to the font to use. * * NOTE: This font is not deleted when the widget is destroyed! */ void CNxWidget::setFont(CNxFont *font) { m_style.font = font; } /** * Draws the visible regions of the widget and the widget's child widgets. */ void CNxWidget::redraw(void) { if (isDrawingEnabled()) { // Get the graphics port needed to draw on this window CGraphicsPort *port = m_widgetControl->getGraphicsPort(); // Draw the Widget drawBorder(port); drawContents(port); // Remember that the widget is no longer erased m_flags.erased = false; // Draw the children of the widget drawChildren(); } } /** * Enables the widget. * * @return True if the widget was enabled. */ bool CNxWidget::enable(void) { if (!m_flags.enabled) { m_flags.enabled = true; onEnable(); redraw(); m_widgetEventHandlers->raiseEnableEvent(); return true; } return false; } /** * Disabled the widget. * * @return True if the widget was disabled. */ bool CNxWidget::disable(void) { if (m_flags.enabled) { m_flags.enabled = false; onDisable(); redraw(); m_widgetEventHandlers->raiseDisableEvent(); return true; } return false; } /** * Erases the widget, marks it as deleted, and moves it to the CNxWidget * deletion queue. Widgets are automatically deleted by the framework and * should not be deleted externally. */ void CNxWidget::close(void) { if (!m_flags.deleted) { m_widgetEventHandlers->raiseCloseEvent(); m_widgetEventHandlers->disable(); m_flags.deleted = true; m_flags.drawingEnabled = false; // Unset clicked widget if necessary CNxWidget *clickedWidget = m_widgetControl->getClickedWidget(); if (clickedWidget == this) { release(clickedWidget->getX(), clickedWidget->getY()); } if (m_parent != (CNxWidget *)NULL) { m_parent->closeChild(this); } } } /** * Draws the widget and makes it visible. * Does not steal focus from other widgets. * * @return True if the widget was shown. * @see hide() */ bool CNxWidget::show(void) { if (m_flags.hidden) { m_flags.hidden = false; m_widgetEventHandlers->raiseShowEvent(); redraw(); return true; } return false; } /** * Erases the widget and makes it invisible. * Does not re-assign focus to another widget. * * @return True if the widget was hidden. * @see show() */ bool CNxWidget::hide(void) { if (!m_flags.hidden) { m_flags.hidden = true; m_widgetEventHandlers->raiseHideEvent(); return true; } return false; } /** * Click this widget at the supplied coordinates. This should only be * overridden in subclasses if the default click behaviour needs to be changed. * If the subclassed widget should just respond to a standard click, * the onClick() method should be overridden instead. * * @param x X coordinate of the click. * @param y Y coordinate of the click. * @return True if the click was successful. */ bool CNxWidget::click(nxgl_coord_t x, nxgl_coord_t y) { if (!isEnabled() || !checkCollision(x, y)) { return false; } // Check for a double-click if (isDoubleClick(x, y)) { return doubleClick(x, y); } // Work out which child was clicked for (int i = m_children.size() - 1; i > -1; i--) { if (m_children[i]->click(x, y)) { return true; } } // Handle clicks on this m_flags.clicked = true; // Record data for double-click clock_gettime(CLOCK_REALTIME, &m_lastClickTime); m_lastClickX = x; m_lastClickY = y; // Take focus away from child widgets setFocusedWidget((CNxWidget *)NULL); // Tell controlling widget that the clicked widget has changed m_widgetControl->setClickedWidget(this); // Run any code in the inherited class onClick(x, y); m_widgetEventHandlers->raiseClickEvent(x, y); return true; } /** * Check if the click is a double-click. * * @param x X coordinate of the click. * @param y Y coordinate of the click. * @return True if the click is a double-click. */ bool CNxWidget::isDoubleClick(nxgl_coord_t x, nxgl_coord_t y) { // Check for a double-click if (m_flags.doubleClickable && hasFocus() && m_widgetControl->doubleClick()) { // Within the allowed region? if ((m_lastClickX > x - m_doubleClickBounds) && (m_lastClickX < x + m_doubleClickBounds)) { if ((m_lastClickY > y - m_doubleClickBounds) && (m_lastClickY < y + m_doubleClickBounds)) { return true; } } } return false; } /** * Double-click this widget at the supplied coordinates. This * should only be overridden in subclasses if the default * double-click behaviour needs to be changed. If the subclassed * widget should just respond to a standard double-click, the * onDoubleClick() method should be overridden instead. * * @param x X coordinate of the click. * @param y Y coordinate of the click. * @return True if the click was successful. */ bool CNxWidget::doubleClick(nxgl_coord_t x, nxgl_coord_t y) { if (!isEnabled() || !checkCollision(x, y)) { return false; } // Work out which child was clicked. Allow the // child to determine if it has been double-clicked or not // in case the second click has fallen on a different // child to the first. for (int i = m_children.size() - 1; i > -1; i--) { if (m_children[i]->click(x, y)) { return true; } } m_flags.clicked = true; // Record data for double-click clock_gettime(CLOCK_REALTIME, &m_lastClickTime); m_lastClickX = x; m_lastClickY = y; // Take focus away from child widgets setFocusedWidget((CNxWidget *)NULL); // Tell controlling widget that the clicked widget has changed m_widgetControl->setClickedWidget(this); onDoubleClick(x, y); m_widgetEventHandlers->raiseDoubleClickEvent(x, y); return true; } /** * Release this widget at the supplied coordinates. This should only be * overridden in subclasses if the default release behaviour needs to be * changed. If the subclassed widget should just respond to a standard * release, the onRelease() method should be overridden instead. * * @param x X coordinate of the release. * @param y Y coordinate of the release. * @return True if the release was successful. */ bool CNxWidget::release(nxgl_coord_t x, nxgl_coord_t y) { if (!m_flags.clicked) { return false; } // Notify of the pre-release event. In this event, the widget is still in the // clicked state. This event is used, for example, by button handlers so // that the click event is really processed when the button is released // instead of pressed. The former behavior is needed because the result // of processing the press may obscure the button and make it impossible // to receive the release event. onPreRelease(x, y); // Now mark the widget as NOT clicked and stop dragging actions. m_flags.clicked = false; stopDragging(x, y); if (m_widgetControl->getClickedWidget() == this) { m_widgetControl->setClickedWidget((CNxWidget *)NULL); } // Determine which release event to fire if (checkCollision(x, y)) { // Notify of the release event... the widget was NOT dragged outside of // its original bounding box onRelease(x, y); // Release occurred within widget; raise release m_widgetEventHandlers->raiseReleaseEvent(x, y); } else { // Notify of the release event... the widget WAS dragged outside of // its original bounding box onReleaseOutside(x, y); // Release occurred outside widget; raise release outside event m_widgetEventHandlers->raiseReleaseOutsideEvent(x, y); } return true; } /** * Drag the widget to the supplied coordinates. * * @param x The x coordinate of the mouse. * @param y The y coordinate of the mouse. * @param vX The horizontal distance that the mouse was dragged. * @param vY The vertical distance that the mouse was dragged. * @return True if the drag was successful. */ bool CNxWidget::drag(nxgl_coord_t x, nxgl_coord_t y, nxgl_coord_t vX, nxgl_coord_t vY) { if (isEnabled() && m_flags.dragging) { if (vX != 0 || vY != 0) { onDrag(x, y, vX, vY); m_widgetEventHandlers->raiseDragEvent(x, y, vX, vY); } return true; } return false; } /** * Send a keypress to the widget. * * @param key The keycode to send to the widget. * @return True if the keypress was processed. */ bool CNxWidget::keyPress(nxwidget_char_t key) { if (!isEnabled()) { return false; } // Raise keypress for this widget m_widgetEventHandlers->raiseKeyPressEvent(key); // Handle active child if (m_focusedChild != NULL) { m_focusedChild->keyPress(key); } return true; } /** * Send a cursor control event to the widget. * * @param cursorControl The cursor control code o send to the widget. * @return True if the cursor control was processed. */ bool CNxWidget::cursorControl(ECursorControl control) { if (!isEnabled()) { return false; } // Raise cursor control for this widget m_widgetEventHandlers->raiseCursorControlEvent(control); // Handle active child if (m_focusedChild != NULL) { m_focusedChild->cursorControl(control); } return true; } /** * Give the widget focus. * * @return True if the widget received focus correctly. */ bool CNxWidget::focus(void) { if (!isEnabled()) { return false; } // Remember if the widget has focus bool hadFocus = m_flags.hasFocus; m_flags.hasFocus = true; // Notify parent that this widget has focus if (m_parent != (CNxWidget *)NULL) { m_parent->setFocusedWidget(this); } // Raise an event only if the widget did not have focus if (!hadFocus) { onFocus(); m_widgetEventHandlers->raiseFocusEvent(); return true; } return false; } /** * Remove focus from the widget. * * @return True if the widget lost focus correctly. */ bool CNxWidget::blur(void) { // Remember if the widget had focus bool hadFocus = m_flags.hasFocus; m_flags.hasFocus = false; // Take focus away from child widgets if (m_focusedChild != (CNxWidget *)NULL) { m_focusedChild->blur(); m_focusedChild = (CNxWidget *)NULL; m_widgetControl->clearFocusedWidget(this); } // Raise an event only if the widget had focus if (hadFocus) { onBlur(); m_widgetEventHandlers->raiseBlurEvent(); return true; } return false; } /** * Move the widget to the new coordinates. * Co-ordinates are relative to the parent widget. * * @param x The new x coordinate. * @param y The new y coordinate. * @return True if the move was successful. */ bool CNxWidget::moveTo(nxgl_coord_t x, nxgl_coord_t y) { // Enforce widget to stay within parent confines if necessary if (m_parent != (CNxWidget *)NULL) { if (!m_parent->isPermeable()) { CRect parentRect; m_parent->getClientRect(parentRect); // Check x coordinate if (x < parentRect.getX()) { x = parentRect.getX(); // Check width against new value if (x + getWidth() > parentRect.getX2() + 1) { return false; } } else if (x + getWidth() > parentRect.getX2() + 1) { x = (parentRect.getX() + parentRect.getX()) - getWidth(); // Check new x value if (x < parentRect.getX()) { return false; } } // Check y coordinate if (y < parentRect.getY()) { y = parentRect.getY(); // Check height against new value if (y + getHeight() > parentRect.getY2() + 1) { return false; } } else if (y + getHeight() > parentRect.getY2() + 1) { y = (parentRect.getY() + parentRect.getY()) - getHeight(); // Check new y value if (y < parentRect.getY()) { return false; } } } } // Perform move if necessary if ((m_rect.getX() != x) || (m_rect.getY() != y)) { nxgl_coord_t oldX = m_rect.getX(); nxgl_coord_t oldY = m_rect.getY(); m_rect.setX(x); m_rect.setY(y); redraw(); m_widgetEventHandlers->raiseMoveEvent(x, y, x - oldX, y - oldY); return true; } return false; } /** * Resize the widget to the new dimensions. * * @param width The new width. * @param height The new height. * @return True if the resize was successful. */ bool CNxWidget::resize(nxgl_coord_t width, nxgl_coord_t height) { // Enforce widget to stay within parent confines if necessary if (m_parent != (CNxWidget *)NULL) { if (!m_parent->isPermeable()) { CRect parentRect; m_parent->getClientRect(parentRect); // Check width if (m_rect.getX() + width > parentRect.getX2() + 1) { width = parentRect.getX2() + 1 - m_rect.getX(); } // Check height if (m_rect.getY() + height > parentRect.getY2() + 1) { height = parentRect.getY2() + 1 - m_rect.getY(); } } } if (getWidth() != width || getHeight() != height) { // Remember if the widget is permeable bool wasPermeable = m_flags.permeable; // Remember if widget was drawing bool wasDrawEnabled = m_flags.drawingEnabled; m_flags.permeable = true; disableDrawing(); m_rect.setWidth(width); m_rect.setHeight(height); onResize(width, height); // Reset the permeable value m_flags.permeable = wasPermeable; // Reset drawing value m_flags.drawingEnabled = wasDrawEnabled; redraw(); m_widgetEventHandlers->raiseResizeEvent(width, height); return true; } return false; } /** * Resize and move the widget in one operation. * Only performs one redraw so it is faster than calling the * two separate functions. * * @param x The new x coordinate. * @param y The new y coordinate. * @param width The new width. * @param height The new height. * @return True if the widget was adjusted successfully. */ bool CNxWidget::changeDimensions(nxgl_coord_t x, nxgl_coord_t y, nxgl_coord_t width, nxgl_coord_t height) { bool wasDrawing = m_flags.drawingEnabled; m_flags.drawingEnabled = false; bool moved = moveTo(x, y); m_flags.drawingEnabled = wasDrawing; return (resize(width, height) | moved); } /** * Moves the supplied child widget to the deletion queue. * For framework use only. * * @param widget A pointer to the child widget. */ void CNxWidget::moveChildToDeleteQueue(CNxWidget *widget) { // Locate widget in main vector for (int i = 0; i < m_children.size(); i++) { if (m_children[i] == widget) { // Add widget to controlling widget's delete vector m_widgetControl->addToDeleteQueue(widget); // Remove widget from main vector m_children.erase(i); break; } } } /** * Sets the supplied widget as the focused child. The widget must * be a child of this widget. * * @param widget A pointer to the child widget. * @see getFocusedWidget() */ void CNxWidget::setFocusedWidget(CNxWidget *widget) { if (m_focusedChild != widget) { if (m_focusedChild != NULL) { // Blur the current active widget m_focusedChild->blur(); } } // Remember the new active widget m_focusedChild = widget; // Make this widget active too focus(); // Route keyboard input to the focused widget m_widgetControl->setFocusedWidget(this); } /** * Checks if the supplied coordinates collide with this widget. * * @param x The x coordinate to check. * @param y The y coordinate to check. * @return True if a collision occurred. */ bool CNxWidget::checkCollision(nxgl_coord_t x, nxgl_coord_t y) const { if (isHidden()) { return false; } // Get the clipped rect CRect rect; getRect(rect); return rect.contains(x, y); } /** * Checks if the supplied rectangle definition collides with this widget. * * @param x The x coordinate of the rectangle to check. * @param y The y coordinate of the rectangle to check. * @param width The width of the rectangle to check. * @param height The height of the rectangle to check. * @return True if a collision occurred. */ bool CNxWidget::checkCollision(nxgl_coord_t x, nxgl_coord_t y, nxgl_coord_t width, nxgl_coord_t height) const { if (isHidden()) { return false; } // Get the clipped rect CRect rect; getRect(rect); return rect.intersects(CRect(x, y, width, height)); } /** * Checks if the supplied widget collides with this widget. * * @param widget A pointer to another widget to check for collisions with. * @return True if a collision occurred. */ bool CNxWidget::checkCollision(CNxWidget *widget) const { // Get the clipped rect CRect rect; widget->getRect(rect); return rect.intersects(m_rect); } /** * Adds a widget to this widget's child stack. The widget is added to the * top of the stack. Note that the widget can only be added if it is not * already a child of another widget. * * @param widget A pointer to the widget to add to the child list. * @see insertWidget() */ void CNxWidget::addWidget(CNxWidget *widget) { if (widget->getParent() == NULL) { widget->setParent(this); m_children.push_back(widget); // Should the widget steal the focus? if (widget->hasFocus()) { setFocusedWidget(widget); } widget->enableDrawing(); widget->redraw(); } } /** * Inserts a widget into this widget's child stack at the bottom of the * stack. Note that the widget can only be added if it is not already * a child of another widget. * * @param widget A pointer to the widget to add to the child list. * @see addWidget() */ void CNxWidget::insertWidget(CNxWidget *widget) { if (widget->getParent() == NULL) { widget->setParent(this); m_children.insert(0, widget); widget->enableDrawing(); widget->redraw(); } } /** * Remove this widget from the widget hierarchy. Returns * responsibility for deleting the widget back to the developer. * Does not unregister the widget from the VBL system. * Does not erase the widget from the display. * * @return True if the widget was successfully removed. */ bool CNxWidget::remove(void) { if (m_parent != (CNxWidget *)NULL) { return m_parent->removeChild(this); } return false; } /** * Remove a child widget from the widget hierarchy. Returns * responsibility for deleting the widget back to the developer. * Does not unregister the widget from the VBL system. * Does not erase the widget from the display. * * @param widget Pointer to the widget to remove from the hierarchy. * @return True if the widget was succesfully removed. */ bool CNxWidget::removeChild(CNxWidget *widget) { // Do we need to make another widget active? if (m_focusedChild == widget) { m_focusedChild = (CNxWidget *)NULL; m_widgetControl->clearFocusedWidget(this); } // Unset clicked widget if necessary CNxWidget *clickedWidget = m_widgetControl->getClickedWidget(); if (clickedWidget == widget) { clickedWidget->release(clickedWidget->getX(), clickedWidget->getY()); } // Divorce child from parent widget->setParent((CNxWidget *)NULL); widget->disableDrawing(); // Locate widget in main vector for (int i = 0; i < m_children.size(); i++) { if (m_children[i] == widget) { // Remove widget from main vector m_children.erase(i); return true; } } return false; } /** * Get the child widget at the specified index. * * @param index Index of the child to retrieve. * @return Pointer to the child at the specified index. */ const CNxWidget *CNxWidget::getChild(int index) const { if (index < (int)m_children.size()) { return m_children[index]; } return (CNxWidget *)NULL; } /** * Sets the border size. The border cannot be drawn over in the * drawContents() method. * * @param borderSize The new border size. */ void CNxWidget::setBorderSize(const WidgetBorderSize &borderSize) { m_borderSize.top = borderSize.top; m_borderSize.right = borderSize.right; m_borderSize.bottom = borderSize.bottom; m_borderSize.left = borderSize.left; } /** * Use the provided widget style */ void CNxWidget::useWidgetStyle(const CWidgetStyle *style) { m_style.colors.background = style->colors.background; m_style.colors.selectedBackground = style->colors.selectedBackground; m_style.colors.shineEdge = style->colors.shineEdge; m_style.colors.shadowEdge = style->colors.shadowEdge; m_style.colors.highlight = style->colors.highlight; m_style.colors.disabledText = style->colors.disabledText; m_style.colors.enabledText = style->colors.enabledText; m_style.colors.selectedText = style->colors.selectedText; m_style.font = style->font; } /** * Draw the area of this widget that falls within the clipping region. * Called by the redraw() function to draw all visible regions. * * @param port The CGraphicsPort to draw to. * @see redraw(). */ void CNxWidget::drawContents(CGraphicsPort* port) { port->drawFilledRect(getX(), getY(), getWidth(), getHeight(), getBackgroundColor()); } /** * Draw all visible regions of this widget's children. */ void CNxWidget::drawChildren(void) { for (int i = 0; i < m_children.size(); i++) { m_children[i]->redraw(); } } /** * Remove the supplied child widget from this widget and send it to * the deletion queue. * * @param widget The widget to close. * @see close(). */ void CNxWidget::closeChild(CNxWidget *widget) { if (widget == NULL) { return; } // Ensure widget knows it is being closed widget->close(); // Do we need to make another widget active? if (m_focusedChild == widget) { m_focusedChild = (CNxWidget *)NULL; m_widgetControl->clearFocusedWidget(this); // Try to choose highest widget for (int i = m_children.size() - 1; i > -1; i--) { if ((m_children[i] != widget) && (!m_children[i]->isHidden())) { m_focusedChild = m_children[i]; } } // Where should the focus go? if (m_focusedChild != NULL) { // Send focus to the new active widget m_focusedChild->focus(); // Route keyboard input to the focused widget m_widgetControl->setFocusedWidget(this); } else { // Give focus to this setFocusedWidget((CNxWidget *)NULL); } } moveChildToDeleteQueue(widget); } /** * Notify this widget that it is being dragged, and set its drag point. * * @param x The x coordinate of the drag position relative to this widget. * @param y The y coordinate of the drag position relative to this widget. */ void CNxWidget::startDragging(nxgl_coord_t x, nxgl_coord_t y) { if (m_flags.draggable) { m_flags.dragging = true; m_flags.clicked = true; m_grabPointX = x - getX(); m_grabPointY = y - getY(); m_newX = m_rect.getX(); m_newY = m_rect.getY(); onDragStart(); } } /** * Notify this widget that it is no longer being dragged. */ void CNxWidget::stopDragging(nxgl_coord_t x, nxgl_coord_t y) { if (m_flags.dragging) { onDragStop(); m_flags.dragging = false; m_widgetEventHandlers->raiseDropEvent(x, y); } }