XNA - handling mouse clicks

Started by
1 comment, last by glaeken 14 years, 7 months ago
Hi, My head is in a flat spin on this one. Essentially, the issue is thus: I have a custom class for a collapsible panel inside an XNA app I'm writing, and here is the code. It's in a separate file called CollapsiblePanel.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace BillBoardSprite
{
    class CollapsiblePanel
    {

        public struct widget
        {
            public Texture2D widgetTex;
            public Vector2 widgetPos;
            public String tag;
        }

        private Viewport localViewport;
        private Vector2 topXY;
        private Vector2 panelDimensions;
        private bool isCollapsed;
        private List<widget> widgets;
        private Texture2D chevron;
        private Texture2D chevron2;
        private Vector2 chevronPosition;
        private SpriteBatch sBatch;

        public Viewport viewport
        {
            get
            {
                return (localViewport);
            }
            set
            {
                this.localViewport = value;
            }
        }

        public Vector2 panelTopXY
        {
            get
            {
                return (topXY);
            }
            set
            {
                this.topXY = value;
            }
        }

        public Vector2 dimensions
        {
            get
            {
                return (panelDimensions);
            }
            set
            {
                this.panelDimensions = value;
            }
        }        

        public bool collapsed
        {
            get
            {
                return (isCollapsed);
            }
            set
            {
                isCollapsed = value;
            }
        }

        public List<widget> panelWidgets
        {
            get
            {
                return (widgets);
            }
            set
            {
                this.widgets = value;
            }
        }

        public Texture2D panelChevron
        {
            get
            {
                return (chevron);
            }
            set
            {
                this.chevron = value;
            }
        }

        public Texture2D panelChevron2
        {
            get
            {
                return (chevron2);
            }
            set
            {
                this.chevron2 = value;
            }
        }

        public Vector2 chevPosition
        {
            get
            {
                return (chevronPosition);
            }
            set
            {
                this.chevronPosition = value;
            }
        }

        public void toggleCollapse()
        {
            if (isCollapsed == false)
            {
                // Collapse the panel
                isCollapsed = true;
            }
            else if (isCollapsed == true)
            {
                // Uncollapse the panel
                isCollapsed = false;
            }
        }

        public CollapsiblePanel(GraphicsDevice gfx, Vector2 topXY, Vector2 dimensions, Vector2 chevronPosition)
        {
            widgets = new List<widget>();
            this.isCollapsed = false;
            this.topXY = topXY;
            this.panelDimensions = dimensions;
            chevron = Texture2D.FromFile(gfx, "chevron.png");
            this.chevronPosition = chevronPosition;
            sBatch = new SpriteBatch(gfx);
        }

        public void drawPanel(GraphicsDevice g, Viewport v)
        {            
            this.localViewport = v;

            sBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.BackToFront, SaveStateMode.None);

            Texture2D intermediary;
            if (this.isCollapsed)
            {
                intermediary = chevron;                                         
            }
            else
            {
                g.Clear(Color.Peru);
                intermediary = chevron2;
                if (widgets.Count > 0)
                {
                    foreach (widget foo in widgets)
                    {
                        sBatch.Draw(foo.widgetTex, new Rectangle((int)foo.widgetPos.X, (int)foo.widgetPos.Y, foo.widgetTex.Width, foo.widgetTex.Height), Color.White);                        
                    }
                }       
            }
            
            sBatch.Draw(intermediary, new Rectangle((int)chevronPosition.X, (int)chevronPosition.Y, chevron.Width, chevron.Height), Color.White);

            sBatch.End();
        }

        // Checks if the mouse has landed on any of the widgets or the chevron
        public void checkForHits(int mouseX, int mouseY)
        {
            // Chevron
            if (((mouseX > chevPosition.X) && (mouseX < (chevPosition.X + panelChevron.Width)) &&
                    ((mouseY > chevPosition.Y) && (mouseY < (chevPosition.Y + panelChevron.Height)))))
            {
                this.toggleCollapse();
            }

            // Everything else - need to link events to this somehow
            // Only check this if the panel isn't collapsed

            if (!isCollapsed)
            {
                foreach (widget w in widgets)
                {
                    if (((mouseX > w.widgetPos.X) && (mouseX < w.widgetTex.Width)) && ((mouseY > w.widgetPos.Y) && (mouseY < (w.widgetPos.Y + w.widgetTex.Height))))
                    {
                        // NEED TO DO THE DELEGATE STUFF HERE
                    }
                }
            }
        }
    } 
}


This works - the panel is collapsible and the clicks on the maximise/minimise chevron is detected correctly. However, observe "NEED TO DO THE DELEGATE STUFF HERE" for a moment. What I'm trying to achieve is to use the tag property inside the widget struct so that I can know which button was clicked and act accordingly. The issue is this: how can I pass the tag back to Game1.cs for the click to be acted upon? I think it might be possible to use some kind of delegate or event but I don't have a clue how to actually implement it. The idea is that the tag will be returned and the correct action can be carried out within Game1.cs. The idea is that clicking the "widget", contained within this object, will change something else inside Game1.cs but I don't know how to glue the two together. Any help you can offer would be greatly appreciated, and thanks in advance. ukd.
Advertisement
Something like this?

// Inside CollapsiblePanelpublic EventHandler<TagType> OnFoo;// In checkForHits()foreach (widget w in widgets){    if (...)    {        if (OnFoo != null)                    OnFoo(this, w.Tag);    }}// In Game1void HandleFoo(object sender, TagType tag){    //...}// This could go in the constructorcPanel.OnFoo += HandleFoo;


Also, look into automatic properties. It will save you a lot of boilerplate code.

EDIT: The code now checks that the event is not null before invoking it. I always forget to do that...
Edit: Gage64 beat me to it.

An event is perfect for this kind of situation.

You can add an event handler to you CollapsiblePanel handler. Now you can create your own delegate type and create an event handler with that signature, or you can just use the standard EventHandler and pass your widget in as the source. You then subscribe to this event in your Game class.

In CollapsiblePanel
//create a delegate with signature (return void, params Widget)public delegate void PanelClickEventHandler(Widget widget);class CollapsiblePanel {   public event PanelClickEventHandlerPanelClickEvent;   //in checkForHits()   if(widget hit && PanelClickEvent!= null)        PanelClickEvent(widget);}


In your Game class
collapsiblePanel.PanelClickEvent += new PanelClickEventHandler(OnPanelClickEvent);void OnPanelClickEvent(Widget widget){   //do some special handling }


Resources:
Delegates
Events

This topic is closed to new replies.

Advertisement