using System; using System.Collections.Generic; using System.Drawing; using Gwen.Anim; using Gwen.DragDrop; using Gwen.Input; namespace Gwen.Control { /// /// Canvas control. It should be the root parent for all other controls. /// public class Canvas : Base { private bool m_NeedsRedraw; private float m_Scale; private Color m_BackgroundColor; // [omeg] these are not created by us, so no disposing internal Base FirstTab; internal Base NextTab; private readonly List m_DisposeQueue; // dictionary for faster access? /// /// Scale for rendering. /// public float Scale { get { return m_Scale; } set { if (m_Scale == value) return; m_Scale = value; if (Skin != null && Skin.Renderer != null) Skin.Renderer.Scale = m_Scale; OnScaleChanged(); Redraw(); } } /// /// Background color. /// public Color BackgroundColor { get { return m_BackgroundColor; } set { m_BackgroundColor = value; } } /// /// In most situations you will be rendering the canvas every frame. /// But in some situations you will only want to render when there have been changes. /// You can do this by checking NeedsRedraw. /// public bool NeedsRedraw { get { return m_NeedsRedraw; } set { m_NeedsRedraw = value; } } /// /// Initializes a new instance of the class. /// /// Skin to use. public Canvas(Skin.Base skin) { SetBounds(0, 0, 10000, 10000); SetSkin(skin); Scale = 1.0f; BackgroundColor = Color.White; ShouldDrawBackground = false; m_DisposeQueue = new List(); } public override void Dispose() { ProcessDelayedDeletes(); base.Dispose(); } /// /// Re-renders the control, invalidates cached texture. /// public override void Redraw() { NeedsRedraw = true; base.Redraw(); } // Children call parent.GetCanvas() until they get to // this top level function. public override Canvas GetCanvas() { return this; } /// /// Additional initialization (which is sometimes not appropriate in the constructor) /// protected void Initialize() { } /// /// Renders the canvas. Call in your rendering loop. /// public void RenderCanvas() { DoThink(); Renderer.Base render = Skin.Renderer; render.Begin(); RecurseLayout(Skin); render.ClipRegion = Bounds; render.RenderOffset = Point.Empty; render.Scale = Scale; if (ShouldDrawBackground) { render.DrawColor = m_BackgroundColor; render.DrawFilledRect(RenderBounds); } DoRender(Skin); DragAndDrop.RenderOverlay(this, Skin); Gwen.ToolTip.RenderToolTip(Skin); render.EndClip(); render.End(); } /// /// Renders the control using specified skin. /// /// Skin to use. protected override void Render(Skin.Base skin) { //skin.Renderer.rnd = new Random(1); base.Render(skin); m_NeedsRedraw = false; } /// /// Handler invoked when control's bounds change. /// /// Old bounds. protected override void OnBoundsChanged(Rectangle oldBounds) { base.OnBoundsChanged(oldBounds); InvalidateChildren(true); } /// /// Processes input and layout. Also purges delayed delete queue. /// private void DoThink() { if (IsHidden) return; Animation.GlobalThink(); // Reset tabbing NextTab = null; FirstTab = null; ProcessDelayedDeletes(); // Check has focus etc.. RecurseLayout(Skin); // If we didn't have a next tab, cycle to the start. if (NextTab == null) NextTab = FirstTab; InputHandler.OnCanvasThink(this); } /// /// Adds given control to the delete queue and detaches it from canvas. Don't call from Dispose, it modifies child list. /// /// Control to delete. public void AddDelayedDelete(Base control) { if (!m_DisposeQueue.Contains(control)) { m_DisposeQueue.Add(control); RemoveChild(control, false); } #if DEBUG else throw new InvalidOperationException("Control deleted twice"); #endif } private void ProcessDelayedDeletes() { //if (m_DisposeQueue.Count > 0) // System.Diagnostics.Debug.Print("Canvas.ProcessDelayedDeletes: {0} items", m_DisposeQueue.Count); foreach (IDisposable control in m_DisposeQueue) { control.Dispose(); } m_DisposeQueue.Clear(); } /// /// Handles mouse movement events. Called from Input subsystems. /// /// True if handled. public bool Input_MouseMoved(int x, int y, int dx, int dy) { if (IsHidden) return false; // Todo: Handle scaling here.. //float fScale = 1.0f / Scale(); InputHandler.OnMouseMoved(this, x, y, dx, dy); if (InputHandler.HoveredControl == null) return false; if (InputHandler.HoveredControl == this) return false; if (InputHandler.HoveredControl.GetCanvas() != this) return false; InputHandler.HoveredControl.InputMouseMoved(x, y, dx, dy); InputHandler.HoveredControl.UpdateCursor(); DragAndDrop.OnMouseMoved(InputHandler.HoveredControl, x, y); return true; } /// /// Handles mouse button events. Called from Input subsystems. /// /// True if handled. public bool Input_MouseButton(int button, bool down) { if (IsHidden) return false; return InputHandler.OnMouseClicked(this, button, down); } /// /// Handles keyboard events. Called from Input subsystems. /// /// True if handled. public bool Input_Key(Key key, bool down) { if (IsHidden) return false; if (key <= Key.Invalid) return false; if (key >= Key.Count) return false; return InputHandler.OnKeyEvent(this, key, down); } /// /// Handles keyboard events. Called from Input subsystems. /// /// True if handled. public bool Input_Character(char chr) { if (IsHidden) return false; if (char.IsControl(chr)) return false; //Handle Accelerators if (InputHandler.HandleAccelerator(this, chr)) return true; //Handle characters if (InputHandler.KeyboardFocus == null) return false; if (InputHandler.KeyboardFocus.GetCanvas() != this) return false; if (!InputHandler.KeyboardFocus.IsVisible) return false; if (InputHandler.IsControlDown) return false; return InputHandler.KeyboardFocus.InputChar(chr); } /// /// Handles the mouse wheel events. Called from Input subsystems. /// /// True if handled. public bool Input_MouseWheel(int val) { if (IsHidden) return false; if (InputHandler.HoveredControl == null) return false; if (InputHandler.HoveredControl == this) return false; if (InputHandler.HoveredControl.GetCanvas() != this) return false; return InputHandler.HoveredControl.InputMouseWheeled(val); } } }