initial commit
Contains changes from "gwen-dotnet" removing dependancies on Windows, which ultimately means certain features (e.g. file load/save dialogs) do not work. Those classes still exist, but the code has been commented out.
This commit is contained in:
commit
10e057953e
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
GwenCS.userprefs
|
||||
Gwen/bin
|
118
Gwen/Align.cs
Normal file
118
Gwen/Align.cs
Normal file
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class for manipulating control's position according to its parent. Rarely needed, use control.Dock.
|
||||
/// </summary>
|
||||
public static class Align
|
||||
{
|
||||
/// <summary>
|
||||
/// Centers the control inside its parent.
|
||||
/// </summary>
|
||||
/// <param name="control">Control to center.</param>
|
||||
public static void Center(Base control)
|
||||
{
|
||||
Base parent = control.Parent;
|
||||
if (parent == null)
|
||||
return;
|
||||
control.SetPosition(
|
||||
parent.Padding.Left + (((parent.Width - parent.Padding.Left - parent.Padding.Right) - control.Width)/2),
|
||||
(parent.Height - control.Height)/2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the control to the left of its parent.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
public static void AlignLeft(Base control)
|
||||
{
|
||||
Base parent = control.Parent;
|
||||
if (null == parent) return;
|
||||
|
||||
control.SetPosition(parent.Padding.Left, control.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Centers the control horizontally inside its parent.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
public static void CenterHorizontally(Base control)
|
||||
{
|
||||
Base parent = control.Parent;
|
||||
if (null == parent) return;
|
||||
|
||||
|
||||
control.SetPosition(parent.Padding.Left + (((parent.Width - parent.Padding.Left - parent.Padding.Right) - control.Width) / 2), control.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the control to the right of its parent.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
public static void AlignRight(Base control)
|
||||
{
|
||||
Base parent = control.Parent;
|
||||
if (null == parent) return;
|
||||
|
||||
|
||||
control.SetPosition(parent.Width - control.Width - parent.Padding.Right, control.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the control to the top of its parent.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
public static void AlignTop(Base control)
|
||||
{
|
||||
control.SetPosition(control.X, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Centers the control vertically inside its parent.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
public static void CenterVertically(Base control)
|
||||
{
|
||||
Base parent = control.Parent;
|
||||
if (null == parent) return;
|
||||
|
||||
control.SetPosition(control.X, (parent.Height - control.Height) / 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the control to the bottom of its parent.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
public static void AlignBottom(Base control)
|
||||
{
|
||||
Base parent = control.Parent;
|
||||
if (null == parent) return;
|
||||
|
||||
control.SetPosition(control.X, parent.Height - control.Height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Places the control below other control (left aligned), taking margins into account.
|
||||
/// </summary>
|
||||
/// <param name="control">Control to place.</param>
|
||||
/// <param name="anchor">Anchor control.</param>
|
||||
/// <param name="spacing">Optional spacing.</param>
|
||||
public static void PlaceDownLeft(Base control, Base anchor, int spacing = 0)
|
||||
{
|
||||
control.SetPosition(anchor.X, anchor.Bottom + spacing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Places the control to the right of other control (bottom aligned), taking margins into account.
|
||||
/// </summary>
|
||||
/// <param name="control">Control to place.</param>
|
||||
/// <param name="anchor">Anchor control.</param>
|
||||
/// <param name="spacing">Optional spacing.</param>
|
||||
public static void PlaceRightBottom(Base control, Base anchor, int spacing = 0)
|
||||
{
|
||||
control.SetPosition(anchor.Right + spacing, anchor.Y - control.Height + anchor.Height);
|
||||
}
|
||||
}
|
||||
}
|
57
Gwen/Anim/Animation.cs
Normal file
57
Gwen/Anim/Animation.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.Anim
|
||||
{
|
||||
public class Animation
|
||||
{
|
||||
protected Base m_Control;
|
||||
|
||||
//private static List<Animation> g_AnimationsListed = new List<Animation>(); // unused
|
||||
private static readonly Dictionary<Base, List<Animation>> m_Animations = new Dictionary<Base, List<Animation>>();
|
||||
|
||||
protected virtual void Think()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual bool Finished
|
||||
{
|
||||
get { throw new InvalidOperationException("Pure virtual function call"); }
|
||||
}
|
||||
|
||||
public static void Add(Base control, Animation animation)
|
||||
{
|
||||
animation.m_Control = control;
|
||||
if (!m_Animations.ContainsKey(control))
|
||||
m_Animations[control] = new List<Animation>();
|
||||
m_Animations[control].Add(animation);
|
||||
}
|
||||
|
||||
public static void Cancel(Base control)
|
||||
{
|
||||
if (m_Animations.ContainsKey(control))
|
||||
{
|
||||
m_Animations[control].Clear();
|
||||
m_Animations.Remove(control);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void GlobalThink()
|
||||
{
|
||||
foreach (KeyValuePair<Base, List<Animation>> pair in m_Animations)
|
||||
{
|
||||
var valCopy = pair.Value.FindAll(x =>true); // list copy so foreach won't break when we remove elements
|
||||
foreach (Animation animation in valCopy)
|
||||
{
|
||||
animation.Think();
|
||||
if (animation.Finished)
|
||||
{
|
||||
pair.Value.Remove(animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
Gwen/Anim/Size/Height.cs
Normal file
38
Gwen/Anim/Size/Height.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Anim.Size
|
||||
{
|
||||
class Height : TimedAnimation
|
||||
{
|
||||
private int m_StartSize;
|
||||
private int m_Delta;
|
||||
private bool m_Hide;
|
||||
|
||||
public Height(int startSize, int endSize, float length, bool hide = false, float delay = 0.0f, float ease = 1.0f)
|
||||
: base(length, delay, ease)
|
||||
{
|
||||
m_StartSize = startSize;
|
||||
m_Delta = endSize - m_StartSize;
|
||||
m_Hide = hide;
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
m_Control.Height = m_StartSize;
|
||||
}
|
||||
|
||||
protected override void Run(float delta)
|
||||
{
|
||||
base.Run(delta);
|
||||
m_Control.Height = (int)(m_StartSize + (m_Delta * delta));
|
||||
}
|
||||
|
||||
protected override void OnFinish()
|
||||
{
|
||||
base.OnFinish();
|
||||
m_Control.Height = m_StartSize + m_Delta;
|
||||
m_Control.IsHidden = m_Hide;
|
||||
}
|
||||
}
|
||||
}
|
38
Gwen/Anim/Size/Width.cs
Normal file
38
Gwen/Anim/Size/Width.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Anim.Size
|
||||
{
|
||||
class Width : TimedAnimation
|
||||
{
|
||||
private int m_StartSize;
|
||||
private int m_Delta;
|
||||
private bool m_Hide;
|
||||
|
||||
public Width(int startSize, int endSize, float length, bool hide = false, float delay = 0.0f, float ease = 1.0f)
|
||||
: base(length, delay, ease)
|
||||
{
|
||||
m_StartSize = startSize;
|
||||
m_Delta = endSize - m_StartSize;
|
||||
m_Hide = hide;
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
m_Control.Width = m_StartSize;
|
||||
}
|
||||
|
||||
protected override void Run(float delta)
|
||||
{
|
||||
base.Run(delta);
|
||||
m_Control.Width = (int)Math.Round(m_StartSize + (m_Delta * delta));
|
||||
}
|
||||
|
||||
protected override void OnFinish()
|
||||
{
|
||||
base.OnFinish();
|
||||
m_Control.Width = m_StartSize + m_Delta;
|
||||
m_Control.IsHidden = m_Hide;
|
||||
}
|
||||
}
|
||||
}
|
69
Gwen/Anim/TimedAnimation.cs
Normal file
69
Gwen/Anim/TimedAnimation.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Anim
|
||||
{
|
||||
// Timed animation. Provides a useful base for animations.
|
||||
public class TimedAnimation : Animation
|
||||
{
|
||||
private bool m_Started;
|
||||
private bool m_Finished;
|
||||
private float m_Start;
|
||||
private float m_End;
|
||||
private float m_Ease;
|
||||
|
||||
public override bool Finished { get { return m_Finished; } }
|
||||
|
||||
public TimedAnimation(float length, float delay = 0.0f, float ease = 1.0f)
|
||||
{
|
||||
m_Start = Platform.Neutral.GetTimeInSeconds() + delay;
|
||||
m_End = m_Start + length;
|
||||
m_Ease = ease;
|
||||
m_Started = false;
|
||||
m_Finished = false;
|
||||
}
|
||||
|
||||
protected override void Think()
|
||||
{
|
||||
//base.Think();
|
||||
|
||||
if (m_Finished)
|
||||
return;
|
||||
|
||||
float current = Platform.Neutral.GetTimeInSeconds();
|
||||
float secondsIn = current - m_Start;
|
||||
if (secondsIn < 0.0)
|
||||
return;
|
||||
|
||||
if (!m_Started)
|
||||
{
|
||||
m_Started = true;
|
||||
OnStart();
|
||||
}
|
||||
|
||||
float delta = secondsIn / (m_End - m_Start);
|
||||
if (delta < 0.0f)
|
||||
delta = 0.0f;
|
||||
if (delta > 1.0f)
|
||||
delta = 1.0f;
|
||||
|
||||
Run((float)Math.Pow(delta, m_Ease));
|
||||
|
||||
if (delta == 1.0f)
|
||||
{
|
||||
m_Finished = true;
|
||||
OnFinish();
|
||||
}
|
||||
}
|
||||
|
||||
// These are the magic functions you should be overriding
|
||||
|
||||
protected virtual void OnStart()
|
||||
{ }
|
||||
|
||||
protected virtual void Run(float delta)
|
||||
{ }
|
||||
|
||||
protected virtual void OnFinish()
|
||||
{ }
|
||||
}
|
||||
}
|
2126
Gwen/Control/Base.cs
Normal file
2126
Gwen/Control/Base.cs
Normal file
File diff suppressed because it is too large
Load diff
318
Gwen/Control/Button.cs
Normal file
318
Gwen/Control/Button.cs
Normal file
|
@ -0,0 +1,318 @@
|
|||
using System;
|
||||
using Gwen.Input;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Button control.
|
||||
/// </summary>
|
||||
public class Button : Label
|
||||
{
|
||||
private bool m_Depressed;
|
||||
private bool m_Toggle;
|
||||
private bool m_ToggleStatus;
|
||||
private bool m_CenterImage;
|
||||
private ImagePanel m_Image;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the button is released.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Clicked;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the button is pressed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Pressed;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the button is released.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Released;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the button's toggle state has changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Toggled;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the button's toggle state has changed to On.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ToggledOn;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the button's toggle state has changed to Off.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ToggledOff;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the button has been double clicked.
|
||||
/// </summary>
|
||||
public event GwenEventHandler DoubleClickedLeft;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the button is depressed.
|
||||
/// </summary>
|
||||
public bool IsDepressed
|
||||
{
|
||||
get { return m_Depressed; }
|
||||
set
|
||||
{
|
||||
if (m_Depressed == value)
|
||||
return;
|
||||
m_Depressed = value;
|
||||
Redraw();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the button is toggleable.
|
||||
/// </summary>
|
||||
public bool IsToggle { get { return m_Toggle; } set { m_Toggle = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines the button's toggle state.
|
||||
/// </summary>
|
||||
public bool ToggleState
|
||||
{
|
||||
get { return m_ToggleStatus; }
|
||||
set
|
||||
{
|
||||
if (!m_Toggle) return;
|
||||
if (m_ToggleStatus == value) return;
|
||||
|
||||
m_ToggleStatus = value;
|
||||
|
||||
if (Toggled != null)
|
||||
Toggled.Invoke(this);
|
||||
|
||||
if (m_ToggleStatus)
|
||||
{
|
||||
if (ToggledOn != null)
|
||||
ToggledOn.Invoke(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ToggledOff != null)
|
||||
ToggledOff.Invoke(this);
|
||||
}
|
||||
|
||||
Redraw();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Control constructor.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Button(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetSize(100, 20);
|
||||
MouseInputEnabled = true;
|
||||
Alignment = Pos.Center;
|
||||
TextPadding = new Padding(3, 3, 3, 3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the button.
|
||||
/// </summary>
|
||||
public virtual void Toggle()
|
||||
{
|
||||
ToggleState = !ToggleState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// "Clicks" the button.
|
||||
/// </summary>
|
||||
public virtual void Press(Base control = null)
|
||||
{
|
||||
OnClicked();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
base.Render(skin);
|
||||
|
||||
if (ShouldDrawBackground)
|
||||
{
|
||||
bool drawDepressed = IsDepressed && IsHovered;
|
||||
if (IsToggle)
|
||||
drawDepressed = drawDepressed || ToggleState;
|
||||
|
||||
bool bDrawHovered = IsHovered && ShouldDrawHover;
|
||||
|
||||
skin.DrawButton(this, drawDepressed, bDrawHovered, IsDisabled);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="down">If set to <c>true</c> mouse button is down.</param>
|
||||
protected override void OnMouseClickedLeft(int x, int y, bool down)
|
||||
{
|
||||
//base.OnMouseClickedLeft(x, y, down);
|
||||
if (down)
|
||||
{
|
||||
IsDepressed = true;
|
||||
InputHandler.MouseFocus = this;
|
||||
if (Pressed != null)
|
||||
Pressed.Invoke(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsHovered && m_Depressed)
|
||||
{
|
||||
OnClicked();
|
||||
}
|
||||
|
||||
IsDepressed = false;
|
||||
InputHandler.MouseFocus = null;
|
||||
if (Released != null)
|
||||
Released.Invoke(this);
|
||||
}
|
||||
|
||||
Redraw();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal OnPressed implementation.
|
||||
/// </summary>
|
||||
protected virtual void OnClicked()
|
||||
{
|
||||
if (IsToggle)
|
||||
{
|
||||
Toggle();
|
||||
}
|
||||
|
||||
if (Clicked != null)
|
||||
Clicked.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the button's image.
|
||||
/// </summary>
|
||||
/// <param name="textureName">Texture name. Null to remove.</param>
|
||||
/// <param name="center">Determines whether the image should be centered.</param>
|
||||
public virtual void SetImage(String textureName, bool center = false)
|
||||
{
|
||||
if (String.IsNullOrEmpty(textureName))
|
||||
{
|
||||
if (m_Image != null)
|
||||
m_Image.Dispose();
|
||||
m_Image = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Image == null)
|
||||
{
|
||||
m_Image = new ImagePanel(this);
|
||||
}
|
||||
|
||||
m_Image.ImageName = textureName;
|
||||
m_Image.SizeToContents( );
|
||||
m_Image.SetPosition(Math.Max(Padding.Left, 2), 2);
|
||||
m_CenterImage = center;
|
||||
|
||||
TextPadding = new Padding(m_Image.Right + 2, TextPadding.Top, TextPadding.Right, TextPadding.Bottom);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sizes to contents.
|
||||
/// </summary>
|
||||
public override void SizeToContents()
|
||||
{
|
||||
base.SizeToContents();
|
||||
if (m_Image != null)
|
||||
{
|
||||
int height = m_Image.Height + 4;
|
||||
if (Height < height)
|
||||
{
|
||||
Height = height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Space keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeySpace(bool down)
|
||||
{
|
||||
if (down)
|
||||
OnClicked();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default accelerator handler.
|
||||
/// </summary>
|
||||
protected override void OnAccelerator()
|
||||
{
|
||||
OnClicked();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
base.Layout(skin);
|
||||
if (m_Image != null)
|
||||
{
|
||||
Align.CenterVertically(m_Image);
|
||||
|
||||
if (m_CenterImage)
|
||||
Align.CenterHorizontally(m_Image);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates control colors.
|
||||
/// </summary>
|
||||
public override void UpdateColors()
|
||||
{
|
||||
if (IsDisabled)
|
||||
{
|
||||
TextColor = Skin.Colors.Button.Disabled;
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsDepressed || ToggleState)
|
||||
{
|
||||
TextColor = Skin.Colors.Button.Down;
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsHovered)
|
||||
{
|
||||
TextColor = Skin.Colors.Button.Hover;
|
||||
return;
|
||||
}
|
||||
|
||||
TextColor = Skin.Colors.Button.Normal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse double click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
protected override void OnMouseDoubleClickedLeft(int x, int y)
|
||||
{
|
||||
OnMouseClickedLeft(x, y, true);
|
||||
if (DoubleClickedLeft != null)
|
||||
DoubleClickedLeft.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
298
Gwen/Control/Canvas.cs
Normal file
298
Gwen/Control/Canvas.cs
Normal file
|
@ -0,0 +1,298 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using Gwen.Anim;
|
||||
using Gwen.DragDrop;
|
||||
using Gwen.Input;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Canvas control. It should be the root parent for all other controls.
|
||||
/// </summary>
|
||||
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<IDisposable> m_DisposeQueue; // dictionary for faster access?
|
||||
|
||||
/// <summary>
|
||||
/// Scale for rendering.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Background color.
|
||||
/// </summary>
|
||||
public Color BackgroundColor { get { return m_BackgroundColor; } set { m_BackgroundColor = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public bool NeedsRedraw { get { return m_NeedsRedraw; } set { m_NeedsRedraw = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Canvas"/> class.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
public Canvas(Skin.Base skin)
|
||||
{
|
||||
SetBounds(0, 0, 10000, 10000);
|
||||
SetSkin(skin);
|
||||
Scale = 1.0f;
|
||||
BackgroundColor = Color.White;
|
||||
ShouldDrawBackground = false;
|
||||
|
||||
m_DisposeQueue = new List<IDisposable>();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
ProcessDelayedDeletes();
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-renders the control, invalidates cached texture.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Additional initialization (which is sometimes not appropriate in the constructor)
|
||||
/// </summary>
|
||||
protected void Initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the canvas. Call in your rendering loop.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
//skin.Renderer.rnd = new Random(1);
|
||||
base.Render(skin);
|
||||
m_NeedsRedraw = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked when control's bounds change.
|
||||
/// </summary>
|
||||
/// <param name="oldBounds">Old bounds.</param>
|
||||
protected override void OnBoundsChanged(Rectangle oldBounds)
|
||||
{
|
||||
base.OnBoundsChanged(oldBounds);
|
||||
InvalidateChildren(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes input and layout. Also purges delayed delete queue.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds given control to the delete queue and detaches it from canvas. Don't call from Dispose, it modifies child list.
|
||||
/// </summary>
|
||||
/// <param name="control">Control to delete.</param>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles mouse movement events. Called from Input subsystems.
|
||||
/// </summary>
|
||||
/// <returns>True if handled.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles mouse button events. Called from Input subsystems.
|
||||
/// </summary>
|
||||
/// <returns>True if handled.</returns>
|
||||
public bool Input_MouseButton(int button, bool down)
|
||||
{
|
||||
if (IsHidden) return false;
|
||||
|
||||
return InputHandler.OnMouseClicked(this, button, down);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles keyboard events. Called from Input subsystems.
|
||||
/// </summary>
|
||||
/// <returns>True if handled.</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles keyboard events. Called from Input subsystems.
|
||||
/// </summary>
|
||||
/// <returns>True if handled.</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the mouse wheel events. Called from Input subsystems.
|
||||
/// </summary>
|
||||
/// <returns>True if handled.</returns>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
113
Gwen/Control/CheckBox.cs
Normal file
113
Gwen/Control/CheckBox.cs
Normal file
|
@ -0,0 +1,113 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// CheckBox control.
|
||||
/// </summary>
|
||||
public class CheckBox : Button
|
||||
{
|
||||
private bool m_Checked;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the checkbox is checked.
|
||||
/// </summary>
|
||||
public bool IsChecked
|
||||
{
|
||||
get { return m_Checked; }
|
||||
set
|
||||
{
|
||||
if (m_Checked == value) return;
|
||||
m_Checked = value;
|
||||
OnCheckChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CheckBox"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public CheckBox(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetSize(15, 15);
|
||||
//m_Checked = true; // [omeg] why?!
|
||||
//Toggle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the checkbox.
|
||||
/// </summary>
|
||||
public override void Toggle()
|
||||
{
|
||||
base.Toggle();
|
||||
IsChecked = !IsChecked;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the checkbox has been checked.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Checked;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the checkbox has been unchecked.
|
||||
/// </summary>
|
||||
public event GwenEventHandler UnChecked;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the checkbox state has been changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler CheckChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether unchecking is allowed.
|
||||
/// </summary>
|
||||
protected virtual bool AllowUncheck { get { return true; } }
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CheckChanged event.
|
||||
/// </summary>
|
||||
protected virtual void OnCheckChanged()
|
||||
{
|
||||
if (IsChecked)
|
||||
{
|
||||
if (Checked != null)
|
||||
Checked.Invoke(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UnChecked != null)
|
||||
UnChecked.Invoke(this);
|
||||
}
|
||||
|
||||
if (CheckChanged != null)
|
||||
CheckChanged.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
base.Render(skin);
|
||||
skin.DrawCheckBox(this, m_Checked, IsDepressed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal OnPressed implementation.
|
||||
/// </summary>
|
||||
protected override void OnClicked()
|
||||
{
|
||||
if (IsDisabled)
|
||||
return;
|
||||
|
||||
if (IsChecked && !AllowUncheck)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Toggle();
|
||||
}
|
||||
}
|
||||
}
|
176
Gwen/Control/CollapsibleCategory.cs
Normal file
176
Gwen/Control/CollapsibleCategory.cs
Normal file
|
@ -0,0 +1,176 @@
|
|||
using System;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// CollapsibleCategory control. Used in CollapsibleList.
|
||||
/// </summary>
|
||||
public class CollapsibleCategory : Base
|
||||
{
|
||||
private readonly Button m_HeaderButton;
|
||||
private readonly CollapsibleList m_List;
|
||||
|
||||
/// <summary>
|
||||
/// Header text.
|
||||
/// </summary>
|
||||
public String Text { get { return m_HeaderButton.Text; } set { m_HeaderButton.Text = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the category is collapsed (closed).
|
||||
/// </summary>
|
||||
public bool IsCollapsed { get { return m_HeaderButton.ToggleState; } set { m_HeaderButton.ToggleState = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when an entry has been selected.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Selected;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the category collapsed state has been changed (header button has been pressed).
|
||||
/// </summary>
|
||||
public event GwenEventHandler Collapsed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CollapsibleCategory"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public CollapsibleCategory(CollapsibleList parent) : base(parent)
|
||||
{
|
||||
m_HeaderButton = new CategoryHeaderButton(this);
|
||||
m_HeaderButton.Text = "Category Title"; // [omeg] todo: i18n
|
||||
m_HeaderButton.Dock = Pos.Top;
|
||||
m_HeaderButton.Height = 20;
|
||||
m_HeaderButton.Toggled += OnHeaderToggle;
|
||||
|
||||
m_List = parent;
|
||||
|
||||
Padding = new Padding(1, 0, 1, 5);
|
||||
SetSize(512, 512);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected entry.
|
||||
/// </summary>
|
||||
public Button GetSelectedButton()
|
||||
{
|
||||
foreach (Base child in Children)
|
||||
{
|
||||
CategoryButton button = child as CategoryButton;
|
||||
if (button == null)
|
||||
continue;
|
||||
|
||||
if (button.ToggleState)
|
||||
return button;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for header button toggle event.
|
||||
/// </summary>
|
||||
/// <param name="control">Source control.</param>
|
||||
protected virtual void OnHeaderToggle(Base control)
|
||||
{
|
||||
if (Collapsed != null)
|
||||
Collapsed.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Selected event.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source.</param>
|
||||
protected virtual void OnSelected(Base control)
|
||||
{
|
||||
CategoryButton child = control as CategoryButton;
|
||||
if (child == null) return;
|
||||
|
||||
if (m_List != null)
|
||||
{
|
||||
m_List.UnselectAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
UnselectAll();
|
||||
}
|
||||
|
||||
child.ToggleState = true;
|
||||
|
||||
if (Selected != null)
|
||||
Selected.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new entry.
|
||||
/// </summary>
|
||||
/// <param name="name">Entry name (displayed).</param>
|
||||
/// <returns>Newly created control.</returns>
|
||||
public Button Add(String name)
|
||||
{
|
||||
CategoryButton button = new CategoryButton(this);
|
||||
button.Text = name;
|
||||
button.Dock = Pos.Top;
|
||||
button.SizeToContents();
|
||||
button.SetSize(button.Width + 4, button.Height + 4);
|
||||
button.Padding = new Padding(5, 2, 2, 2);
|
||||
button.Clicked += OnSelected;
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawCategoryInner(this, m_HeaderButton.ToggleState);
|
||||
base.Render(skin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unselects all entries.
|
||||
/// </summary>
|
||||
public void UnselectAll()
|
||||
{
|
||||
foreach (Base child in Children)
|
||||
{
|
||||
CategoryButton button = child as CategoryButton;
|
||||
if (button == null)
|
||||
continue;
|
||||
|
||||
button.ToggleState = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function invoked after layout.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void PostLayout(Skin.Base skin)
|
||||
{
|
||||
if (IsCollapsed)
|
||||
{
|
||||
Height = m_HeaderButton.Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
SizeToChildren(false, true);
|
||||
}
|
||||
|
||||
// alternate row coloring
|
||||
bool b = true;
|
||||
foreach (Base child in Children)
|
||||
{
|
||||
CategoryButton button = child as CategoryButton;
|
||||
if (button == null)
|
||||
continue;
|
||||
|
||||
button.m_Alt = b;
|
||||
button.UpdateColors();
|
||||
b = !b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
130
Gwen/Control/CollapsibleList.cs
Normal file
130
Gwen/Control/CollapsibleList.cs
Normal file
|
@ -0,0 +1,130 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// CollapsibleList control. Groups CollapsibleCategory controls.
|
||||
/// </summary>
|
||||
public class CollapsibleList : ScrollControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked when an entry has been selected.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ItemSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a category collapsed state has been changed (header button has been pressed).
|
||||
/// </summary>
|
||||
public event GwenEventHandler CategoryCollapsed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CollapsibleList"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public CollapsibleList(Base parent) : base(parent)
|
||||
{
|
||||
EnableScroll(false, true);
|
||||
AutoHideBars = true;
|
||||
}
|
||||
|
||||
// todo: iterator, make this as function? check if works
|
||||
|
||||
/// <summary>
|
||||
/// Selected entry.
|
||||
/// </summary>
|
||||
public Button GetSelectedButton()
|
||||
{
|
||||
foreach (Base child in Children)
|
||||
{
|
||||
CollapsibleCategory cat = child as CollapsibleCategory;
|
||||
if (cat == null)
|
||||
continue;
|
||||
|
||||
Button button = cat.GetSelectedButton();
|
||||
|
||||
if (button != null)
|
||||
return button;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a category to the list.
|
||||
/// </summary>
|
||||
/// <param name="category">Category control to add.</param>
|
||||
protected virtual void Add(CollapsibleCategory category)
|
||||
{
|
||||
category.Parent = this;
|
||||
category.Dock = Pos.Top;
|
||||
category.Margin = new Margin(1, 0, 1, 1);
|
||||
category.Selected += OnCategorySelected;
|
||||
category.Collapsed += OnCategoryCollapsed;
|
||||
// this relies on fact that category.m_List is set to its parent
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new category to the list.
|
||||
/// </summary>
|
||||
/// <param name="categoryName">Name of the category.</param>
|
||||
/// <returns>Newly created control.</returns>
|
||||
public virtual CollapsibleCategory Add(String categoryName)
|
||||
{
|
||||
CollapsibleCategory cat = new CollapsibleCategory(this);
|
||||
cat.Text = categoryName;
|
||||
Add(cat);
|
||||
return cat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawCategoryHolder(this);
|
||||
base.Render(skin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unselects all entries.
|
||||
/// </summary>
|
||||
public virtual void UnselectAll()
|
||||
{
|
||||
foreach (Base child in Children)
|
||||
{
|
||||
CollapsibleCategory cat = child as CollapsibleCategory;
|
||||
if (cat == null)
|
||||
continue;
|
||||
|
||||
cat.UnselectAll();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for ItemSelected event.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source: <see cref="CollapsibleList"/>.</param>
|
||||
protected virtual void OnCategorySelected(Base control)
|
||||
{
|
||||
CollapsibleCategory cat = control as CollapsibleCategory;
|
||||
if (cat == null) return;
|
||||
|
||||
if (ItemSelected != null)
|
||||
ItemSelected.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for category collapsed event.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source: <see cref="CollapsibleCategory"/>.</param>
|
||||
protected virtual void OnCategoryCollapsed(Base control)
|
||||
{
|
||||
CollapsibleCategory cat = control as CollapsibleCategory;
|
||||
if (cat == null) return;
|
||||
|
||||
if (CategoryCollapsed != null)
|
||||
CategoryCollapsed.Invoke(control);
|
||||
}
|
||||
}
|
||||
}
|
207
Gwen/Control/ColorLerpBox.cs
Normal file
207
Gwen/Control/ColorLerpBox.cs
Normal file
|
@ -0,0 +1,207 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.Input;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Linear-interpolated HSV color box.
|
||||
/// </summary>
|
||||
public class ColorLerpBox : Base
|
||||
{
|
||||
private Point m_CursorPos;
|
||||
private bool m_Depressed;
|
||||
private float m_Hue;
|
||||
private Texture m_Texture;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the selected color has been changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ColorChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ColorLerpBox"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public ColorLerpBox(Base parent) : base(parent)
|
||||
{
|
||||
SetColor(Color.FromArgb(255, 255, 128, 0));
|
||||
SetSize(128, 128);
|
||||
MouseInputEnabled = true;
|
||||
m_Depressed = false;
|
||||
|
||||
// texture is initialized in Render() if null
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
if (m_Texture != null)
|
||||
m_Texture.Dispose();
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Linear color interpolation.
|
||||
/// </summary>
|
||||
public static Color Lerp(Color toColor, Color fromColor, float amount)
|
||||
{
|
||||
Color delta = toColor.Subtract(fromColor);
|
||||
delta = delta.Multiply(amount);
|
||||
return fromColor.Add(delta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selected color.
|
||||
/// </summary>
|
||||
public Color SelectedColor
|
||||
{
|
||||
get { return GetColorAt(m_CursorPos.X, m_CursorPos.Y); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the selected color.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to set.</param>
|
||||
/// <param name="onlyHue">Deetrmines whether to only set H value (not SV).</param>
|
||||
public void SetColor(Color value, bool onlyHue = true)
|
||||
{
|
||||
HSV hsv = value.ToHSV();
|
||||
m_Hue = hsv.h;
|
||||
|
||||
if (!onlyHue)
|
||||
{
|
||||
m_CursorPos.X = (int)(hsv.s * Width);
|
||||
m_CursorPos.Y = (int)((1 - hsv.v) * Height);
|
||||
}
|
||||
Invalidate();
|
||||
|
||||
if (ColorChanged != null)
|
||||
ColorChanged.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse moved event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="dx">X change.</param>
|
||||
/// <param name="dy">Y change.</param>
|
||||
protected override void OnMouseMoved(int x, int y, int dx, int dy)
|
||||
{
|
||||
if (m_Depressed)
|
||||
{
|
||||
m_CursorPos = CanvasPosToLocal(new Point(x, y));
|
||||
//Do we have clamp?
|
||||
if (m_CursorPos.X < 0)
|
||||
m_CursorPos.X = 0;
|
||||
if (m_CursorPos.X > Width)
|
||||
m_CursorPos.X = Width;
|
||||
|
||||
if (m_CursorPos.Y < 0)
|
||||
m_CursorPos.Y = 0;
|
||||
if (m_CursorPos.Y > Height)
|
||||
m_CursorPos.Y = Height;
|
||||
|
||||
if (ColorChanged != null)
|
||||
ColorChanged.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="down">If set to <c>true</c> mouse button is down.</param>
|
||||
protected override void OnMouseClickedLeft(int x, int y, bool down)
|
||||
{
|
||||
m_Depressed = down;
|
||||
if (down)
|
||||
InputHandler.MouseFocus = this;
|
||||
else
|
||||
InputHandler.MouseFocus = null;
|
||||
|
||||
OnMouseMoved(x, y, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color from specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X</param>
|
||||
/// <param name="y">Y</param>
|
||||
/// <returns>Color value.</returns>
|
||||
private Color GetColorAt(int x, int y)
|
||||
{
|
||||
float xPercent = (x / (float)Width);
|
||||
float yPercent = 1 - (y / (float)Height);
|
||||
|
||||
Color result = Util.HSVToColor(m_Hue, xPercent, yPercent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates the control.
|
||||
/// </summary>
|
||||
public override void Invalidate()
|
||||
{
|
||||
if (m_Texture != null)
|
||||
{
|
||||
m_Texture.Dispose();
|
||||
m_Texture = null;
|
||||
}
|
||||
base.Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
if (m_Texture == null)
|
||||
{
|
||||
byte[] pixelData = new byte[Width*Height*4];
|
||||
|
||||
for (int x = 0; x < Width; x++)
|
||||
{
|
||||
for (int y = 0; y < Height; y++)
|
||||
{
|
||||
Color c = GetColorAt(x, y);
|
||||
pixelData[4*(x + y*Width)] = c.R;
|
||||
pixelData[4*(x + y*Width) + 1] = c.G;
|
||||
pixelData[4*(x + y*Width) + 2] = c.B;
|
||||
pixelData[4*(x + y*Width) + 3] = c.A;
|
||||
}
|
||||
}
|
||||
|
||||
m_Texture = new Texture(skin.Renderer);
|
||||
m_Texture.Width = Width;
|
||||
m_Texture.Height = Height;
|
||||
m_Texture.LoadRaw(Width, Height, pixelData);
|
||||
}
|
||||
|
||||
skin.Renderer.DrawColor = Color.White;
|
||||
skin.Renderer.DrawTexturedRect(m_Texture, RenderBounds);
|
||||
|
||||
|
||||
skin.Renderer.DrawColor = Color.Black;
|
||||
skin.Renderer.DrawLinedRect(RenderBounds);
|
||||
|
||||
Color selected = SelectedColor;
|
||||
if ((selected.R + selected.G + selected.B)/3 < 170)
|
||||
skin.Renderer.DrawColor = Color.White;
|
||||
else
|
||||
skin.Renderer.DrawColor = Color.Black;
|
||||
|
||||
Rectangle testRect = new Rectangle(m_CursorPos.X - 3, m_CursorPos.Y - 3, 6, 6);
|
||||
|
||||
skin.Renderer.DrawShavedCornerRect(testRect);
|
||||
|
||||
base.Render(skin);
|
||||
}
|
||||
}
|
||||
}
|
256
Gwen/Control/ColorPicker.cs
Normal file
256
Gwen/Control/ColorPicker.cs
Normal file
|
@ -0,0 +1,256 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// RGBA color picker.
|
||||
/// </summary>
|
||||
public class ColorPicker : Base, IColorPicker
|
||||
{
|
||||
private Color m_Color;
|
||||
|
||||
/// <summary>
|
||||
/// Selected color.
|
||||
/// </summary>
|
||||
public Color SelectedColor { get { return m_Color; } set { m_Color = value; UpdateControls(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Red value of the selected color.
|
||||
/// </summary>
|
||||
public int R { get { return m_Color.R; } set { m_Color = Color.FromArgb(m_Color.A, value, m_Color.G, m_Color.B); } }
|
||||
|
||||
/// <summary>
|
||||
/// Green value of the selected color.
|
||||
/// </summary>
|
||||
public int G { get { return m_Color.G; } set { m_Color = Color.FromArgb(m_Color.A, m_Color.R, value, m_Color.B); } }
|
||||
|
||||
/// <summary>
|
||||
/// Blue value of the selected color.
|
||||
/// </summary>
|
||||
public int B { get { return m_Color.B; } set { m_Color = Color.FromArgb(m_Color.A, m_Color.R, m_Color.G, value); } }
|
||||
|
||||
/// <summary>
|
||||
/// Alpha value of the selected color.
|
||||
/// </summary>
|
||||
public int A { get { return m_Color.A; } set { m_Color = Color.FromArgb(value, m_Color.R, m_Color.G, m_Color.B); } }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the selected color has been changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ColorChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ColorPicker"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public ColorPicker(Base parent) : base(parent)
|
||||
{
|
||||
MouseInputEnabled = true;
|
||||
|
||||
SetSize(256, 150);
|
||||
CreateControls();
|
||||
SelectedColor = Color.FromArgb(255, 50, 60, 70);
|
||||
}
|
||||
|
||||
private void CreateColorControl(String name, int y)
|
||||
{
|
||||
const int colorSize = 12;
|
||||
|
||||
GroupBox colorGroup = new GroupBox(this);
|
||||
colorGroup.SetPosition(10, y);
|
||||
colorGroup.SetText(name);
|
||||
colorGroup.SetSize(160, 35);
|
||||
colorGroup.Name = name + "groupbox";
|
||||
|
||||
ColorDisplay disp = new ColorDisplay(colorGroup);
|
||||
disp.Name = name;
|
||||
disp.SetBounds(0, 10, colorSize, colorSize);
|
||||
|
||||
TextBoxNumeric numeric = new TextBoxNumeric(colorGroup);
|
||||
numeric.Name = name + "Box";
|
||||
numeric.SetPosition(105, 7);
|
||||
numeric.SetSize(26, 16);
|
||||
numeric.SelectAllOnFocus = true;
|
||||
numeric.TextChanged += NumericTyped;
|
||||
|
||||
HorizontalSlider slider = new HorizontalSlider(colorGroup);
|
||||
slider.SetPosition(colorSize + 5, 10);
|
||||
slider.SetRange(0, 255);
|
||||
slider.SetSize(80, colorSize);
|
||||
slider.Name = name + "Slider";
|
||||
slider.ValueChanged += SlidersMoved;
|
||||
}
|
||||
|
||||
private void NumericTyped(Base control)
|
||||
{
|
||||
TextBoxNumeric box = control as TextBoxNumeric;
|
||||
if (null == box)
|
||||
return;
|
||||
|
||||
if (box.Text == string.Empty)
|
||||
return;
|
||||
|
||||
int textValue = (int) box.Value;
|
||||
if (textValue < 0) textValue = 0;
|
||||
if (textValue > 255) textValue = 255;
|
||||
|
||||
if (box.Name.Contains("Red"))
|
||||
R = textValue;
|
||||
|
||||
if (box.Name.Contains("Green"))
|
||||
G = textValue;
|
||||
|
||||
if (box.Name.Contains("Blue"))
|
||||
B = textValue;
|
||||
|
||||
if (box.Name.Contains("Alpha"))
|
||||
A = textValue;
|
||||
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
private void CreateControls()
|
||||
{
|
||||
const int startY = 5;
|
||||
const int height = 35;
|
||||
|
||||
CreateColorControl("Red", startY);
|
||||
CreateColorControl("Green", startY + height);
|
||||
CreateColorControl("Blue", startY + height*2);
|
||||
CreateColorControl("Alpha", startY + height*3);
|
||||
|
||||
GroupBox finalGroup = new GroupBox(this);
|
||||
finalGroup.SetPosition(180, 40);
|
||||
finalGroup.SetSize(60, 60);
|
||||
finalGroup.SetText("Result");
|
||||
finalGroup.Name = "ResultGroupBox";
|
||||
|
||||
ColorDisplay disp = new ColorDisplay(finalGroup);
|
||||
disp.Name = "Result";
|
||||
disp.SetBounds(0, 10, 32, 32);
|
||||
//disp.DrawCheckers = true;
|
||||
|
||||
//UpdateControls();
|
||||
}
|
||||
|
||||
private void UpdateColorControls(String name, Color col, int sliderVal)
|
||||
{
|
||||
ColorDisplay disp = FindChildByName(name, true) as ColorDisplay;
|
||||
disp.Color = col;
|
||||
|
||||
HorizontalSlider slider = FindChildByName(name + "Slider", true) as HorizontalSlider;
|
||||
slider.Value = sliderVal;
|
||||
|
||||
TextBoxNumeric box = FindChildByName(name + "Box", true) as TextBoxNumeric;
|
||||
box.Value = sliderVal;
|
||||
}
|
||||
|
||||
private void UpdateControls()
|
||||
{ //This is a little weird, but whatever for now
|
||||
UpdateColorControls("Red", Color.FromArgb(255, SelectedColor.R, 0, 0), SelectedColor.R);
|
||||
UpdateColorControls("Green", Color.FromArgb(255, 0, SelectedColor.G, 0), SelectedColor.G);
|
||||
UpdateColorControls("Blue", Color.FromArgb(255, 0, 0, SelectedColor.B), SelectedColor.B);
|
||||
UpdateColorControls("Alpha", Color.FromArgb(SelectedColor.A, 255, 255, 255), SelectedColor.A);
|
||||
|
||||
ColorDisplay disp = FindChildByName("Result", true) as ColorDisplay;
|
||||
disp.Color = SelectedColor;
|
||||
|
||||
if (ColorChanged != null)
|
||||
ColorChanged.Invoke(this);
|
||||
}
|
||||
|
||||
private void SlidersMoved(Base control)
|
||||
{
|
||||
/*
|
||||
HorizontalSlider* redSlider = gwen_cast<HorizontalSlider>( FindChildByName( "RedSlider", true ) );
|
||||
HorizontalSlider* greenSlider = gwen_cast<HorizontalSlider>( FindChildByName( "GreenSlider", true ) );
|
||||
HorizontalSlider* blueSlider = gwen_cast<HorizontalSlider>( FindChildByName( "BlueSlider", true ) );
|
||||
HorizontalSlider* alphaSlider = gwen_cast<HorizontalSlider>( FindChildByName( "AlphaSlider", true ) );
|
||||
*/
|
||||
|
||||
HorizontalSlider slider = control as HorizontalSlider;
|
||||
if (slider != null)
|
||||
SetColorByName(GetColorFromName(slider.Name), (int)slider.Value);
|
||||
|
||||
UpdateControls();
|
||||
//SetColor( Gwen::Color( redSlider->GetValue(), greenSlider->GetValue(), blueSlider->GetValue(), alphaSlider->GetValue() ) );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
base.Layout(skin);
|
||||
|
||||
SizeToChildren(false, true);
|
||||
SetSize(Width, Height + 5);
|
||||
|
||||
GroupBox groupBox = FindChildByName("ResultGroupBox", true) as GroupBox;
|
||||
if (groupBox != null)
|
||||
groupBox.SetPosition(groupBox.X, Height * 0.5f - groupBox.Height * 0.5f);
|
||||
|
||||
//UpdateControls(); // this spams events continuously every tick
|
||||
}
|
||||
|
||||
private int GetColorByName(String colorName)
|
||||
{
|
||||
if (colorName == "Red")
|
||||
return SelectedColor.R;
|
||||
if (colorName == "Green")
|
||||
return SelectedColor.G;
|
||||
if (colorName == "Blue")
|
||||
return SelectedColor.B;
|
||||
if (colorName == "Alpha")
|
||||
return SelectedColor.A;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static String GetColorFromName(String name)
|
||||
{
|
||||
if (name.Contains("Red"))
|
||||
return "Red";
|
||||
if (name.Contains("Green"))
|
||||
return "Green";
|
||||
if (name.Contains("Blue"))
|
||||
return "Blue";
|
||||
if (name.Contains("Alpha"))
|
||||
return "Alpha";
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
private void SetColorByName(String colorName, int colorValue)
|
||||
{
|
||||
if (colorName == "Red")
|
||||
R = colorValue;
|
||||
else if (colorName == "Green")
|
||||
G = colorValue;
|
||||
else if (colorName == "Blue")
|
||||
B = colorValue;
|
||||
else if (colorName == "Alpha")
|
||||
A = colorValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the Alpha control is visible.
|
||||
/// </summary>
|
||||
public bool AlphaVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
GroupBox gb = FindChildByName("Alphagroupbox", true) as GroupBox;
|
||||
return !gb.IsHidden;
|
||||
}
|
||||
set
|
||||
{
|
||||
GroupBox gb = FindChildByName("Alphagroupbox", true) as GroupBox;
|
||||
gb.IsHidden = !value;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
152
Gwen/Control/ColorSlider.cs
Normal file
152
Gwen/Control/ColorSlider.cs
Normal file
|
@ -0,0 +1,152 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.Input;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// HSV hue selector.
|
||||
/// </summary>
|
||||
public class ColorSlider : Base
|
||||
{
|
||||
private int m_SelectedDist;
|
||||
private bool m_Depressed;
|
||||
private Texture m_Texture;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the selected color has been changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ColorChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ColorSlider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public ColorSlider(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetSize(32, 128);
|
||||
MouseInputEnabled = true;
|
||||
m_Depressed = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
if (m_Texture != null)
|
||||
m_Texture.Dispose();
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
//Is there any way to move this into skin? Not for now, no idea how we'll "actually" render these
|
||||
|
||||
if (m_Texture == null)
|
||||
{
|
||||
byte[] pixelData = new byte[Width * Height * 4];
|
||||
|
||||
for (int y = 0; y < Height; y++)
|
||||
{
|
||||
Color c = GetColorAtHeight(y);
|
||||
for (int x = 0; x < Width; x++)
|
||||
{
|
||||
pixelData[4 * (x + y * Width)] = c.R;
|
||||
pixelData[4 * (x + y * Width) + 1] = c.G;
|
||||
pixelData[4 * (x + y * Width) + 2] = c.B;
|
||||
pixelData[4 * (x + y * Width) + 3] = c.A;
|
||||
}
|
||||
}
|
||||
|
||||
m_Texture = new Texture(skin.Renderer);
|
||||
m_Texture.Width = Width;
|
||||
m_Texture.Height = Height;
|
||||
m_Texture.LoadRaw(Width, Height, pixelData);
|
||||
}
|
||||
|
||||
skin.Renderer.DrawColor = Color.White;
|
||||
skin.Renderer.DrawTexturedRect(m_Texture, new Rectangle(5, 0, Width-10, Height));
|
||||
|
||||
int drawHeight = m_SelectedDist - 3;
|
||||
|
||||
//Draw our selectors
|
||||
skin.Renderer.DrawColor = Color.Black;
|
||||
skin.Renderer.DrawFilledRect(new Rectangle(0, drawHeight + 2, Width, 1));
|
||||
skin.Renderer.DrawFilledRect(new Rectangle(0, drawHeight, 5, 5));
|
||||
skin.Renderer.DrawFilledRect(new Rectangle(Width - 5, drawHeight, 5, 5));
|
||||
skin.Renderer.DrawColor = Color.White;
|
||||
skin.Renderer.DrawFilledRect(new Rectangle(1, drawHeight + 1, 3, 3));
|
||||
skin.Renderer.DrawFilledRect(new Rectangle(Width - 4, drawHeight + 1, 3, 3));
|
||||
|
||||
base.Render(skin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="down">If set to <c>true</c> mouse button is down.</param>
|
||||
protected override void OnMouseClickedLeft(int x, int y, bool down)
|
||||
{
|
||||
m_Depressed = down;
|
||||
if (down)
|
||||
InputHandler.MouseFocus = this;
|
||||
else
|
||||
InputHandler.MouseFocus = null;
|
||||
|
||||
OnMouseMoved(x, y, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse moved event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="dx">X change.</param>
|
||||
/// <param name="dy">Y change.</param>
|
||||
protected override void OnMouseMoved(int x, int y, int dx, int dy)
|
||||
{
|
||||
if (m_Depressed)
|
||||
{
|
||||
Point cursorPos = CanvasPosToLocal(new Point(x, y));
|
||||
|
||||
if (cursorPos.Y < 0)
|
||||
cursorPos.Y = 0;
|
||||
if (cursorPos.Y > Height)
|
||||
cursorPos.Y = Height;
|
||||
|
||||
m_SelectedDist = cursorPos.Y;
|
||||
if (ColorChanged != null)
|
||||
ColorChanged.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
private Color GetColorAtHeight(int y)
|
||||
{
|
||||
float yPercent = y / (float)Height;
|
||||
return Util.HSVToColor(yPercent * 360, 1, 1);
|
||||
}
|
||||
|
||||
private void SetColor(Color color)
|
||||
{
|
||||
HSV hsv = color.ToHSV();
|
||||
|
||||
m_SelectedDist = (int)(hsv.h / 360 * Height);
|
||||
|
||||
if (ColorChanged != null)
|
||||
ColorChanged.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selected color.
|
||||
/// </summary>
|
||||
public Color SelectedColor { get { return GetColorAtHeight(m_SelectedDist); } set { SetColor(value); } }
|
||||
}
|
||||
}
|
238
Gwen/Control/ComboBox.cs
Normal file
238
Gwen/Control/ComboBox.cs
Normal file
|
@ -0,0 +1,238 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// ComboBox control.
|
||||
/// </summary>
|
||||
public class ComboBox : Button
|
||||
{
|
||||
private readonly Menu m_Menu;
|
||||
private readonly Base m_Button;
|
||||
private MenuItem m_SelectedItem;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the selected item has changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ItemSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the combo menu is open.
|
||||
/// </summary>
|
||||
public bool IsOpen { get { return m_Menu != null && !m_Menu.IsHidden; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ComboBox"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public ComboBox(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetSize(100, 20);
|
||||
m_Menu = new Menu(this);
|
||||
m_Menu.IsHidden = true;
|
||||
m_Menu.IconMarginDisabled = true;
|
||||
m_Menu.IsTabable = false;
|
||||
|
||||
DownArrow arrow = new DownArrow(this);
|
||||
m_Button = arrow;
|
||||
|
||||
Alignment = Pos.Left | Pos.CenterV;
|
||||
Text = String.Empty;
|
||||
Margin = new Margin(3, 0, 0, 0);
|
||||
|
||||
IsTabable = true;
|
||||
KeyboardInputEnabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selected item.
|
||||
/// </summary>
|
||||
/// <remarks>Not just String property, because items also have internal names.</remarks>
|
||||
public Label SelectedItem { get { return m_SelectedItem; } }
|
||||
|
||||
internal override bool IsMenuComponent
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new item.
|
||||
/// </summary>
|
||||
/// <param name="label">Item label (displayed).</param>
|
||||
/// <param name="name">Item name.</param>
|
||||
/// <returns>Newly created control.</returns>
|
||||
public virtual MenuItem AddItem(String label, String name = "")
|
||||
{
|
||||
MenuItem item = m_Menu.AddItem(label, String.Empty);
|
||||
item.Name = name;
|
||||
item.Selected += OnItemSelected;
|
||||
|
||||
if (m_SelectedItem == null)
|
||||
OnItemSelected(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawComboBox(this, IsDepressed, IsOpen);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal Pressed implementation.
|
||||
/// </summary>
|
||||
protected override void OnClicked()
|
||||
{
|
||||
if (IsOpen)
|
||||
{
|
||||
GetCanvas().CloseMenus();
|
||||
return;
|
||||
}
|
||||
|
||||
bool wasMenuHidden = m_Menu.IsHidden;
|
||||
|
||||
GetCanvas().CloseMenus();
|
||||
|
||||
if (wasMenuHidden)
|
||||
{
|
||||
Open();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all items.
|
||||
/// </summary>
|
||||
public virtual void DeleteAll()
|
||||
{
|
||||
if (m_Menu != null)
|
||||
m_Menu.DeleteAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal handler for item selected event.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source.</param>
|
||||
protected virtual void OnItemSelected(Base control)
|
||||
{
|
||||
//Convert selected to a menu item
|
||||
MenuItem item = control as MenuItem;
|
||||
if (null == item) return;
|
||||
|
||||
m_SelectedItem = item;
|
||||
Text = m_SelectedItem.Text;
|
||||
m_Menu.IsHidden = true;
|
||||
|
||||
if (ItemSelected != null)
|
||||
ItemSelected.Invoke(this);
|
||||
|
||||
Focus();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
m_Button.Position(Pos.Right|Pos.CenterV, 4, 0);
|
||||
base.Layout(skin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for losing keyboard focus.
|
||||
/// </summary>
|
||||
protected override void OnLostKeyboardFocus()
|
||||
{
|
||||
TextColor = Color.Black;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for gaining keyboard focus.
|
||||
/// </summary>
|
||||
protected override void OnKeyboardFocus()
|
||||
{
|
||||
//Until we add the blue highlighting again
|
||||
TextColor = Color.Black;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the combo.
|
||||
/// </summary>
|
||||
public virtual void Open()
|
||||
{
|
||||
if (null == m_Menu) return;
|
||||
|
||||
m_Menu.Parent = GetCanvas();
|
||||
m_Menu.IsHidden = false;
|
||||
m_Menu.BringToFront();
|
||||
|
||||
Point p = LocalPosToCanvas(Point.Empty);
|
||||
|
||||
m_Menu.SetBounds(new Rectangle(p.X, p.Y + Height, Width, m_Menu.Height));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the combo.
|
||||
/// </summary>
|
||||
public virtual void Close()
|
||||
{
|
||||
if (m_Menu == null)
|
||||
return;
|
||||
|
||||
m_Menu.Hide();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Down Arrow keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyDown(bool down)
|
||||
{
|
||||
if (down)
|
||||
{
|
||||
var it = m_Menu.Children.FindIndex(x => x == m_SelectedItem);
|
||||
if (it + 1 < m_Menu.Children.Count)
|
||||
OnItemSelected(m_Menu.Children[it + 1]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Up Arrow keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyUp(bool down)
|
||||
{
|
||||
if (down)
|
||||
{
|
||||
var it = m_Menu.Children.FindLastIndex(x => x == m_SelectedItem);
|
||||
if (it - 1 >= 0)
|
||||
OnItemSelected(m_Menu.Children[it - 1]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the focus overlay.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void RenderFocus(Skin.Base skin)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
282
Gwen/Control/CrossSplitter.cs
Normal file
282
Gwen/Control/CrossSplitter.cs
Normal file
|
@ -0,0 +1,282 @@
|
|||
using System;
|
||||
//using System.Windows.Forms;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Splitter control.
|
||||
/// </summary>
|
||||
public class CrossSplitter : Base
|
||||
{
|
||||
private readonly SplitterBar m_VSplitter;
|
||||
private readonly SplitterBar m_HSplitter;
|
||||
private readonly SplitterBar m_CSplitter;
|
||||
|
||||
private readonly Base[] m_Sections;
|
||||
|
||||
private float m_HVal; // 0-1
|
||||
private float m_VVal; // 0-1
|
||||
private int m_BarSize; // pixels
|
||||
|
||||
private int m_ZoomedSection; // 0-3
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when one of the panels has been zoomed (maximized).
|
||||
/// </summary>
|
||||
public event GwenEventHandler PanelZoomed;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when one of the panels has been unzoomed (restored).
|
||||
/// </summary>
|
||||
public event GwenEventHandler PanelUnZoomed;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the zoomed panel has been changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ZoomChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CrossSplitter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public CrossSplitter(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_Sections = new Base[4];
|
||||
|
||||
m_VSplitter = new SplitterBar(this);
|
||||
m_VSplitter.SetPosition(0, 128);
|
||||
m_VSplitter.Dragged += OnVerticalMoved;
|
||||
//m_VSplitter.Cursor = Cursors.SizeNS;
|
||||
|
||||
m_HSplitter = new SplitterBar(this);
|
||||
m_HSplitter.SetPosition(128, 0);
|
||||
m_HSplitter.Dragged += OnHorizontalMoved;
|
||||
//m_HSplitter.Cursor = Cursors.SizeWE;
|
||||
|
||||
m_CSplitter = new SplitterBar(this);
|
||||
m_CSplitter.SetPosition(128, 128);
|
||||
m_CSplitter.Dragged += OnCenterMoved;
|
||||
//m_CSplitter.Cursor = Cursors.SizeAll;
|
||||
|
||||
m_HVal = 0.5f;
|
||||
m_VVal = 0.5f;
|
||||
|
||||
SetPanel(0, null);
|
||||
SetPanel(1, null);
|
||||
SetPanel(2, null);
|
||||
SetPanel(3, null);
|
||||
|
||||
SplitterSize = 5;
|
||||
SplittersVisible = false;
|
||||
|
||||
m_ZoomedSection = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Centers the panels so that they take even amount of space.
|
||||
/// </summary>
|
||||
public void CenterPanels()
|
||||
{
|
||||
m_HVal = 0.5f;
|
||||
m_VVal = 0.5f;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether any of the panels is zoomed.
|
||||
/// </summary>
|
||||
public bool IsZoomed { get { return m_ZoomedSection != -1; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether splitters should be visible.
|
||||
/// </summary>
|
||||
public bool SplittersVisible
|
||||
{
|
||||
get { return m_CSplitter.ShouldDrawBackground; }
|
||||
set
|
||||
{
|
||||
m_CSplitter.ShouldDrawBackground = value;
|
||||
m_VSplitter.ShouldDrawBackground = value;
|
||||
m_HSplitter.ShouldDrawBackground = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the splitter.
|
||||
/// </summary>
|
||||
public int SplitterSize { get { return m_BarSize; } set { m_BarSize = value; } }
|
||||
|
||||
private void UpdateVSplitter()
|
||||
{
|
||||
m_VSplitter.MoveTo(m_VSplitter.X, (Height - m_VSplitter.Height) * (m_VVal));
|
||||
}
|
||||
|
||||
private void UpdateHSplitter()
|
||||
{
|
||||
m_HSplitter.MoveTo( ( Width - m_HSplitter.Width ) * ( m_HVal ), m_HSplitter.Y );
|
||||
}
|
||||
|
||||
private void UpdateCSplitter()
|
||||
{
|
||||
m_CSplitter.MoveTo((Width - m_CSplitter.Width) * (m_HVal), (Height - m_CSplitter.Height) * (m_VVal));
|
||||
}
|
||||
|
||||
protected void OnCenterMoved(Base control)
|
||||
{
|
||||
CalculateValueCenter();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected void OnVerticalMoved(Base control)
|
||||
{
|
||||
m_VVal = CalculateValueVertical();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected void OnHorizontalMoved(Base control)
|
||||
{
|
||||
m_HVal = CalculateValueHorizontal();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
private void CalculateValueCenter()
|
||||
{
|
||||
m_HVal = m_CSplitter.X / (float)(Width - m_CSplitter.Width);
|
||||
m_VVal = m_CSplitter.Y / (float)(Height - m_CSplitter.Height);
|
||||
}
|
||||
|
||||
private float CalculateValueVertical()
|
||||
{
|
||||
return m_VSplitter.Y / (float)(Height - m_VSplitter.Height);
|
||||
}
|
||||
|
||||
private float CalculateValueHorizontal()
|
||||
{
|
||||
return m_HSplitter.X / (float)(Width - m_HSplitter.Width);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
m_VSplitter.SetSize(Width, m_BarSize);
|
||||
m_HSplitter.SetSize(m_BarSize, Height);
|
||||
m_CSplitter.SetSize(m_BarSize, m_BarSize);
|
||||
|
||||
UpdateVSplitter();
|
||||
UpdateHSplitter();
|
||||
UpdateCSplitter();
|
||||
|
||||
if (m_ZoomedSection == -1)
|
||||
{
|
||||
if (m_Sections[0] != null)
|
||||
m_Sections[0].SetBounds(0, 0, m_HSplitter.X, m_VSplitter.Y);
|
||||
|
||||
if (m_Sections[1] != null)
|
||||
m_Sections[1].SetBounds(m_HSplitter.X + m_BarSize, 0, Width - (m_HSplitter.X + m_BarSize), m_VSplitter.Y);
|
||||
|
||||
if (m_Sections[2] != null)
|
||||
m_Sections[2].SetBounds(0, m_VSplitter.Y + m_BarSize, m_HSplitter.X, Height - (m_VSplitter.Y + m_BarSize));
|
||||
|
||||
if (m_Sections[3] != null)
|
||||
m_Sections[3].SetBounds(m_HSplitter.X + m_BarSize, m_VSplitter.Y + m_BarSize, Width - (m_HSplitter.X + m_BarSize), Height - (m_VSplitter.Y + m_BarSize));
|
||||
}
|
||||
else
|
||||
{
|
||||
//This should probably use Fill docking instead
|
||||
m_Sections[m_ZoomedSection].SetBounds(0, 0, Width, Height);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a control to the specific inner section.
|
||||
/// </summary>
|
||||
/// <param name="index">Section index (0-3).</param>
|
||||
/// <param name="panel">Control to assign.</param>
|
||||
public void SetPanel(int index, Base panel)
|
||||
{
|
||||
m_Sections[index] = panel;
|
||||
|
||||
if (panel != null)
|
||||
{
|
||||
panel.Dock = Pos.None;
|
||||
panel.Parent = this;
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specific inner section.
|
||||
/// </summary>
|
||||
/// <param name="index">Section index (0-3).</param>
|
||||
/// <returns>Specified section.</returns>
|
||||
public Base GetPanel(int index)
|
||||
{
|
||||
return m_Sections[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal handler for the zoom changed event.
|
||||
/// </summary>
|
||||
protected void OnZoomChanged()
|
||||
{
|
||||
if (ZoomChanged != null)
|
||||
ZoomChanged.Invoke(this);
|
||||
|
||||
if (m_ZoomedSection == -1)
|
||||
{
|
||||
if (PanelUnZoomed != null)
|
||||
PanelUnZoomed.Invoke(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PanelZoomed != null)
|
||||
PanelZoomed.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximizes the specified panel so it fills the entire control.
|
||||
/// </summary>
|
||||
/// <param name="section">Panel index (0-3).</param>
|
||||
public void Zoom(int section)
|
||||
{
|
||||
UnZoom();
|
||||
|
||||
if (m_Sections[section] != null)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (i != section && m_Sections[i] != null)
|
||||
m_Sections[i].IsHidden = true;
|
||||
}
|
||||
m_ZoomedSection = section;
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
OnZoomChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores the control so all panels are visible.
|
||||
/// </summary>
|
||||
public void UnZoom()
|
||||
{
|
||||
m_ZoomedSection = -1;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (m_Sections[i] != null)
|
||||
m_Sections[i].IsHidden = false;
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
OnZoomChanged();
|
||||
}
|
||||
}
|
||||
}
|
440
Gwen/Control/DockBase.cs
Normal file
440
Gwen/Control/DockBase.cs
Normal file
|
@ -0,0 +1,440 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.ControlInternal;
|
||||
using Gwen.DragDrop;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Base for dockable containers.
|
||||
/// </summary>
|
||||
public class DockBase : Base
|
||||
{
|
||||
private DockBase m_Left;
|
||||
private DockBase m_Right;
|
||||
private DockBase m_Top;
|
||||
private DockBase m_Bottom;
|
||||
private Resizer m_Sizer;
|
||||
|
||||
// Only CHILD dockpanels have a tabcontrol.
|
||||
private DockedTabControl m_DockedTabControl;
|
||||
|
||||
private bool m_DrawHover;
|
||||
private bool m_DropFar;
|
||||
private Rectangle m_HoverRect;
|
||||
|
||||
// todo: dock events?
|
||||
|
||||
/// <summary>
|
||||
/// Control docked on the left side.
|
||||
/// </summary>
|
||||
public DockBase LeftDock { get { return GetChildDock(Pos.Left); } }
|
||||
|
||||
/// <summary>
|
||||
/// Control docked on the right side.
|
||||
/// </summary>
|
||||
public DockBase RightDock { get { return GetChildDock(Pos.Right); } }
|
||||
|
||||
/// <summary>
|
||||
/// Control docked on the top side.
|
||||
/// </summary>
|
||||
public DockBase TopDock { get { return GetChildDock(Pos.Top); } }
|
||||
|
||||
/// <summary>
|
||||
/// Control docked on the bottom side.
|
||||
/// </summary>
|
||||
public DockBase BottomDock { get { return GetChildDock(Pos.Bottom); } }
|
||||
|
||||
public TabControl TabControl { get { return m_DockedTabControl; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DockBase"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public DockBase(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
Padding = Padding.One;
|
||||
SetSize(200, 200);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Space keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeySpace(bool down)
|
||||
{
|
||||
// No action on space (default button action is to press)
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an inner docked control for the specified position.
|
||||
/// </summary>
|
||||
/// <param name="pos">Dock position.</param>
|
||||
protected virtual void SetupChildDock(Pos pos)
|
||||
{
|
||||
if (m_DockedTabControl == null)
|
||||
{
|
||||
m_DockedTabControl = new DockedTabControl(this);
|
||||
m_DockedTabControl.TabRemoved += OnTabRemoved;
|
||||
m_DockedTabControl.TabStripPosition = Pos.Bottom;
|
||||
m_DockedTabControl.TitleBarVisible = true;
|
||||
}
|
||||
|
||||
Dock = pos;
|
||||
|
||||
Pos sizeDir;
|
||||
if (pos == Pos.Right) sizeDir = Pos.Left;
|
||||
else if (pos == Pos.Left) sizeDir = Pos.Right;
|
||||
else if (pos == Pos.Top) sizeDir = Pos.Bottom;
|
||||
else if (pos == Pos.Bottom) sizeDir = Pos.Top;
|
||||
else throw new ArgumentException("Invalid dock", "pos");
|
||||
|
||||
if (m_Sizer != null)
|
||||
m_Sizer.Dispose();
|
||||
m_Sizer = new Resizer(this);
|
||||
m_Sizer.Dock = sizeDir;
|
||||
m_Sizer.ResizeDir = sizeDir;
|
||||
m_Sizer.SetSize(2, 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an inner docked control for the specified position.
|
||||
/// </summary>
|
||||
/// <param name="pos"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual DockBase GetChildDock(Pos pos)
|
||||
{
|
||||
// todo: verify
|
||||
DockBase dock = null;
|
||||
switch (pos)
|
||||
{
|
||||
case Pos.Left:
|
||||
if (m_Left == null)
|
||||
{
|
||||
m_Left = new DockBase(this);
|
||||
m_Left.SetupChildDock(pos);
|
||||
}
|
||||
dock = m_Left;
|
||||
break;
|
||||
|
||||
case Pos.Right:
|
||||
if (m_Right == null)
|
||||
{
|
||||
m_Right = new DockBase(this);
|
||||
m_Right.SetupChildDock(pos);
|
||||
}
|
||||
dock = m_Right;
|
||||
break;
|
||||
|
||||
case Pos.Top:
|
||||
if (m_Top == null)
|
||||
{
|
||||
m_Top = new DockBase(this);
|
||||
m_Top.SetupChildDock(pos);
|
||||
}
|
||||
dock = m_Top;
|
||||
break;
|
||||
|
||||
case Pos.Bottom:
|
||||
if (m_Bottom == null)
|
||||
{
|
||||
m_Bottom = new DockBase(this);
|
||||
m_Bottom.SetupChildDock(pos);
|
||||
}
|
||||
dock = m_Bottom;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dock != null)
|
||||
dock.IsHidden = false;
|
||||
|
||||
return dock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates dock direction from dragdrop coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <returns>Dock direction.</returns>
|
||||
protected virtual Pos GetDroppedTabDirection(int x, int y)
|
||||
{
|
||||
int w = Width;
|
||||
int h = Height;
|
||||
float top = y / (float)h;
|
||||
float left = x / (float)w;
|
||||
float right = (w - x) / (float)w;
|
||||
float bottom = (h - y) / (float)h;
|
||||
float minimum = Math.Min(Math.Min(Math.Min(top, left), right), bottom);
|
||||
|
||||
m_DropFar = (minimum < 0.2f);
|
||||
|
||||
if (minimum > 0.3f)
|
||||
return Pos.Fill;
|
||||
|
||||
if (top == minimum && (null == m_Top || m_Top.IsHidden))
|
||||
return Pos.Top;
|
||||
if (left == minimum && (null == m_Left || m_Left.IsHidden))
|
||||
return Pos.Left;
|
||||
if (right == minimum && (null == m_Right || m_Right.IsHidden))
|
||||
return Pos.Right;
|
||||
if (bottom == minimum && (null == m_Bottom || m_Bottom.IsHidden))
|
||||
return Pos.Bottom;
|
||||
|
||||
return Pos.Fill;
|
||||
}
|
||||
|
||||
public override bool DragAndDrop_CanAcceptPackage(Package p)
|
||||
{
|
||||
// A TAB button dropped
|
||||
if (p.Name == "TabButtonMove")
|
||||
return true;
|
||||
|
||||
// a TAB window dropped
|
||||
if (p.Name == "TabWindowMove")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool DragAndDrop_HandleDrop(Package p, int x, int y)
|
||||
{
|
||||
Point pos = CanvasPosToLocal(new Point(x, y));
|
||||
Pos dir = GetDroppedTabDirection(pos.X, pos.Y);
|
||||
|
||||
DockedTabControl addTo = m_DockedTabControl;
|
||||
if (dir == Pos.Fill && addTo == null)
|
||||
return false;
|
||||
|
||||
if (dir != Pos.Fill)
|
||||
{
|
||||
DockBase dock = GetChildDock(dir);
|
||||
addTo = dock.m_DockedTabControl;
|
||||
|
||||
if (!m_DropFar)
|
||||
dock.BringToFront();
|
||||
else
|
||||
dock.SendToBack();
|
||||
}
|
||||
|
||||
if (p.Name == "TabButtonMove")
|
||||
{
|
||||
TabButton tabButton = DragAndDrop.SourceControl as TabButton;
|
||||
if (null == tabButton)
|
||||
return false;
|
||||
|
||||
addTo.AddPage(tabButton);
|
||||
}
|
||||
|
||||
if (p.Name == "TabWindowMove")
|
||||
{
|
||||
DockedTabControl tabControl = DragAndDrop.SourceControl as DockedTabControl;
|
||||
if (null == tabControl)
|
||||
return false;
|
||||
if (tabControl == addTo)
|
||||
return false;
|
||||
|
||||
tabControl.MoveTabsTo(addTo);
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the control contains any docked children.
|
||||
/// </summary>
|
||||
public virtual bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_DockedTabControl != null && m_DockedTabControl.TabCount > 0) return false;
|
||||
|
||||
if (m_Left != null && !m_Left.IsEmpty) return false;
|
||||
if (m_Right != null && !m_Right.IsEmpty) return false;
|
||||
if (m_Top != null && !m_Top.IsEmpty) return false;
|
||||
if (m_Bottom != null && !m_Bottom.IsEmpty) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnTabRemoved(Base control)
|
||||
{
|
||||
DoRedundancyCheck();
|
||||
DoConsolidateCheck();
|
||||
}
|
||||
|
||||
protected virtual void DoRedundancyCheck()
|
||||
{
|
||||
if (!IsEmpty) return;
|
||||
|
||||
DockBase pDockParent = Parent as DockBase;
|
||||
if (null == pDockParent) return;
|
||||
|
||||
pDockParent.OnRedundantChildDock(this);
|
||||
}
|
||||
|
||||
protected virtual void DoConsolidateCheck()
|
||||
{
|
||||
if (IsEmpty) return;
|
||||
if (null == m_DockedTabControl) return;
|
||||
if (m_DockedTabControl.TabCount > 0) return;
|
||||
|
||||
if (m_Bottom != null && !m_Bottom.IsEmpty)
|
||||
{
|
||||
m_Bottom.m_DockedTabControl.MoveTabsTo(m_DockedTabControl);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Top != null && !m_Top.IsEmpty)
|
||||
{
|
||||
m_Top.m_DockedTabControl.MoveTabsTo(m_DockedTabControl);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Left != null && !m_Left.IsEmpty)
|
||||
{
|
||||
m_Left.m_DockedTabControl.MoveTabsTo(m_DockedTabControl);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Right != null && !m_Right.IsEmpty)
|
||||
{
|
||||
m_Right.m_DockedTabControl.MoveTabsTo(m_DockedTabControl);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnRedundantChildDock(DockBase dock)
|
||||
{
|
||||
dock.IsHidden = true;
|
||||
DoRedundancyCheck();
|
||||
DoConsolidateCheck();
|
||||
}
|
||||
|
||||
public override void DragAndDrop_HoverEnter(Package p, int x, int y)
|
||||
{
|
||||
m_DrawHover = true;
|
||||
}
|
||||
|
||||
public override void DragAndDrop_HoverLeave(Package p)
|
||||
{
|
||||
m_DrawHover = false;
|
||||
}
|
||||
|
||||
public override void DragAndDrop_Hover(Package p, int x, int y)
|
||||
{
|
||||
Point pos = CanvasPosToLocal(new Point(x, y));
|
||||
Pos dir = GetDroppedTabDirection(pos.X, pos.Y);
|
||||
|
||||
if (dir == Pos.Fill)
|
||||
{
|
||||
if (null == m_DockedTabControl)
|
||||
{
|
||||
m_HoverRect = Rectangle.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
m_HoverRect = InnerBounds;
|
||||
return;
|
||||
}
|
||||
|
||||
m_HoverRect = RenderBounds;
|
||||
|
||||
int HelpBarWidth = 0;
|
||||
|
||||
if (dir == Pos.Left)
|
||||
{
|
||||
HelpBarWidth = (int)(m_HoverRect.Width * 0.25f);
|
||||
m_HoverRect.Width = HelpBarWidth;
|
||||
}
|
||||
|
||||
if (dir == Pos.Right)
|
||||
{
|
||||
HelpBarWidth = (int)(m_HoverRect.Width * 0.25f);
|
||||
m_HoverRect.X = m_HoverRect.Width - HelpBarWidth;
|
||||
m_HoverRect.Width = HelpBarWidth;
|
||||
}
|
||||
|
||||
if (dir == Pos.Top)
|
||||
{
|
||||
HelpBarWidth = (int)(m_HoverRect.Height * 0.25f);
|
||||
m_HoverRect.Height = HelpBarWidth;
|
||||
}
|
||||
|
||||
if (dir == Pos.Bottom)
|
||||
{
|
||||
HelpBarWidth = (int)(m_HoverRect.Height * 0.25f);
|
||||
m_HoverRect.Y = m_HoverRect.Height - HelpBarWidth;
|
||||
m_HoverRect.Height = HelpBarWidth;
|
||||
}
|
||||
|
||||
if ((dir == Pos.Top || dir == Pos.Bottom) && !m_DropFar)
|
||||
{
|
||||
if (m_Left != null && m_Left.IsVisible)
|
||||
{
|
||||
m_HoverRect.X += m_Left.Width;
|
||||
m_HoverRect.Width -= m_Left.Width;
|
||||
}
|
||||
|
||||
if (m_Right != null && m_Right.IsVisible)
|
||||
{
|
||||
m_HoverRect.Width -= m_Right.Width;
|
||||
}
|
||||
}
|
||||
|
||||
if ((dir == Pos.Left || dir == Pos.Right) && !m_DropFar)
|
||||
{
|
||||
if (m_Top != null && m_Top.IsVisible)
|
||||
{
|
||||
m_HoverRect.Y += m_Top.Height;
|
||||
m_HoverRect.Height -= m_Top.Height;
|
||||
}
|
||||
|
||||
if (m_Bottom != null && m_Bottom.IsVisible)
|
||||
{
|
||||
m_HoverRect.Height -= m_Bottom.Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders over the actual control (overlays).
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void RenderOver(Skin.Base skin)
|
||||
{
|
||||
if (!m_DrawHover)
|
||||
return;
|
||||
|
||||
Renderer.Base render = skin.Renderer;
|
||||
render.DrawColor = Color.FromArgb(20, 255, 200, 255);
|
||||
render.DrawFilledRect(RenderBounds);
|
||||
|
||||
if (m_HoverRect.Width == 0)
|
||||
return;
|
||||
|
||||
render.DrawColor = Color.FromArgb(100, 255, 200, 255);
|
||||
render.DrawFilledRect(m_HoverRect);
|
||||
|
||||
render.DrawColor = Color.FromArgb(200, 255, 200, 255);
|
||||
render.DrawLinedRect(m_HoverRect);
|
||||
}
|
||||
}
|
||||
}
|
81
Gwen/Control/DockedTabControl.cs
Normal file
81
Gwen/Control/DockedTabControl.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Docked tab control.
|
||||
/// </summary>
|
||||
public class DockedTabControl : TabControl
|
||||
{
|
||||
private readonly TabTitleBar m_TitleBar;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the title bar is visible.
|
||||
/// </summary>
|
||||
public bool TitleBarVisible { get { return !m_TitleBar.IsHidden; } set { m_TitleBar.IsHidden = !value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DockedTabControl"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public DockedTabControl(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
Dock = Pos.Fill;
|
||||
|
||||
m_TitleBar = new TabTitleBar(this);
|
||||
m_TitleBar.Dock = Pos.Top;
|
||||
m_TitleBar.IsHidden = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
TabStrip.IsHidden = (TabCount <= 1);
|
||||
UpdateTitleBar();
|
||||
base.Layout(skin);
|
||||
}
|
||||
|
||||
private void UpdateTitleBar()
|
||||
{
|
||||
if (CurrentButton == null)
|
||||
return;
|
||||
|
||||
m_TitleBar.UpdateFromTab(CurrentButton);
|
||||
}
|
||||
|
||||
public override void DragAndDrop_StartDragging(DragDrop.Package package, int x, int y)
|
||||
{
|
||||
base.DragAndDrop_StartDragging(package, x, y);
|
||||
|
||||
IsHidden = true;
|
||||
// This hiding our parent thing is kind of lousy.
|
||||
Parent.IsHidden = true;
|
||||
}
|
||||
|
||||
public override void DragAndDrop_EndDragging(bool success, int x, int y)
|
||||
{
|
||||
IsHidden = false;
|
||||
if (!success)
|
||||
{
|
||||
Parent.IsHidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void MoveTabsTo(DockedTabControl target)
|
||||
{
|
||||
var children = TabStrip.Children.ToArray(); // copy because collection will be modified
|
||||
foreach (Base child in children)
|
||||
{
|
||||
TabButton button = child as TabButton;
|
||||
if (button == null)
|
||||
continue;
|
||||
target.AddPage(button);
|
||||
}
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
74
Gwen/Control/GroupBox.cs
Normal file
74
Gwen/Control/GroupBox.cs
Normal file
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Group box (container).
|
||||
/// </summary>
|
||||
/// <remarks>Don't use autosize with docking.</remarks>
|
||||
public class GroupBox : Label
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GroupBox"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public GroupBox(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
// Set to true, because it's likely that our
|
||||
// children will want mouse input, and they
|
||||
// can't get it without us..
|
||||
MouseInputEnabled = true;
|
||||
KeyboardInputEnabled = true;
|
||||
|
||||
TextPadding = new Padding(10, 0, 10, 0);
|
||||
Alignment = Pos.Top | Pos.Left;
|
||||
Invalidate();
|
||||
|
||||
m_InnerPanel = new Base(this);
|
||||
m_InnerPanel.Dock = Pos.Fill;
|
||||
m_InnerPanel.Margin = new Margin(5, TextHeight+5, 5, 5);
|
||||
//Margin = new Margin(5, 5, 5, 5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
base.Layout(skin);
|
||||
if (AutoSizeToContents)
|
||||
{
|
||||
DoSizeToContents();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawGroupBox(this, TextX, TextHeight, TextWidth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sizes to contents.
|
||||
/// </summary>
|
||||
public override void SizeToContents()
|
||||
{
|
||||
// we inherit from Label and shouldn't use its method.
|
||||
DoSizeToContents();
|
||||
}
|
||||
|
||||
protected virtual void DoSizeToContents()
|
||||
{
|
||||
m_InnerPanel.SizeToChildren();
|
||||
SizeToChildren();
|
||||
if (Width < TextWidth + TextPadding.Right + TextPadding.Left)
|
||||
Width = TextWidth + TextPadding.Right + TextPadding.Left;
|
||||
}
|
||||
}
|
||||
}
|
198
Gwen/Control/HSVColorPicker.cs
Normal file
198
Gwen/Control/HSVColorPicker.cs
Normal file
|
@ -0,0 +1,198 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// HSV color picker with "before" and "after" color boxes.
|
||||
/// </summary>
|
||||
public class HSVColorPicker : Base, IColorPicker
|
||||
{
|
||||
private readonly ColorLerpBox m_LerpBox;
|
||||
private readonly ColorSlider m_ColorSlider;
|
||||
private readonly ColorDisplay m_Before;
|
||||
private readonly ColorDisplay m_After;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the selected color has changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ColorChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The "before" color.
|
||||
/// </summary>
|
||||
public Color DefaultColor { get { return m_Before.Color; } set { m_Before.Color = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Selected color.
|
||||
/// </summary>
|
||||
public Color SelectedColor { get { return m_LerpBox.SelectedColor; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HSVColorPicker"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public HSVColorPicker(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
MouseInputEnabled = true;
|
||||
SetSize(256, 128);
|
||||
//ShouldCacheToTexture = true;
|
||||
|
||||
m_LerpBox = new ColorLerpBox(this);
|
||||
m_LerpBox.ColorChanged += ColorBoxChanged;
|
||||
m_LerpBox.Dock = Pos.Left;
|
||||
|
||||
m_ColorSlider = new ColorSlider(this);
|
||||
m_ColorSlider.SetPosition(m_LerpBox.Width + 15, 5);
|
||||
m_ColorSlider.ColorChanged += ColorSliderChanged;
|
||||
m_ColorSlider.Dock = Pos.Left;
|
||||
|
||||
m_After = new ColorDisplay(this);
|
||||
m_After.SetSize(48, 24);
|
||||
m_After.SetPosition(m_ColorSlider.X + m_ColorSlider.Width + 15, 5);
|
||||
|
||||
m_Before = new ColorDisplay(this);
|
||||
m_Before.SetSize(48, 24);
|
||||
m_Before.SetPosition(m_After.X, 28);
|
||||
|
||||
int x = m_Before.X;
|
||||
int y = m_Before.Y + 30;
|
||||
|
||||
{
|
||||
Label label = new Label(this);
|
||||
label.SetText("R:");
|
||||
label.SizeToContents();
|
||||
label.SetPosition(x, y);
|
||||
|
||||
TextBoxNumeric numeric = new TextBoxNumeric(this);
|
||||
numeric.Name = "RedBox";
|
||||
numeric.SetPosition(x + 15, y - 1);
|
||||
numeric.SetSize(26, 16);
|
||||
numeric.SelectAllOnFocus = true;
|
||||
numeric.TextChanged += NumericTyped;
|
||||
}
|
||||
|
||||
y += 20;
|
||||
|
||||
{
|
||||
Label label = new Label(this);
|
||||
label.SetText("G:");
|
||||
label.SizeToContents();
|
||||
label.SetPosition(x, y);
|
||||
|
||||
TextBoxNumeric numeric = new TextBoxNumeric(this);
|
||||
numeric.Name = "GreenBox";
|
||||
numeric.SetPosition(x + 15, y - 1);
|
||||
numeric.SetSize(26, 16);
|
||||
numeric.SelectAllOnFocus = true;
|
||||
numeric.TextChanged += NumericTyped;
|
||||
}
|
||||
|
||||
y += 20;
|
||||
|
||||
{
|
||||
Label label = new Label(this);
|
||||
label.SetText("B:");
|
||||
label.SizeToContents();
|
||||
label.SetPosition(x, y);
|
||||
|
||||
TextBoxNumeric numeric = new TextBoxNumeric(this);
|
||||
numeric.Name = "BlueBox";
|
||||
numeric.SetPosition(x + 15, y - 1);
|
||||
numeric.SetSize(26, 16);
|
||||
numeric.SelectAllOnFocus = true;
|
||||
numeric.TextChanged += NumericTyped;
|
||||
}
|
||||
|
||||
SetColor(DefaultColor);
|
||||
}
|
||||
|
||||
private void NumericTyped(Base control)
|
||||
{
|
||||
TextBoxNumeric box = control as TextBoxNumeric;
|
||||
if (null == box) return;
|
||||
|
||||
if (box.Text == String.Empty) return;
|
||||
|
||||
int textValue = (int)box.Value;
|
||||
if (textValue < 0) textValue = 0;
|
||||
if (textValue > 255) textValue = 255;
|
||||
|
||||
Color newColor = SelectedColor;
|
||||
|
||||
if (box.Name.Contains("Red"))
|
||||
{
|
||||
newColor = Color.FromArgb(SelectedColor.A, textValue, SelectedColor.G, SelectedColor.B);
|
||||
}
|
||||
else if (box.Name.Contains("Green"))
|
||||
{
|
||||
newColor = Color.FromArgb(SelectedColor.A, SelectedColor.R, textValue, SelectedColor.B);
|
||||
}
|
||||
else if (box.Name.Contains("Blue"))
|
||||
{
|
||||
newColor = Color.FromArgb(SelectedColor.A, SelectedColor.R, SelectedColor.G, textValue);
|
||||
}
|
||||
else if (box.Name.Contains("Alpha"))
|
||||
{
|
||||
newColor = Color.FromArgb(textValue, SelectedColor.R, SelectedColor.G, SelectedColor.B);
|
||||
}
|
||||
|
||||
SetColor(newColor);
|
||||
}
|
||||
|
||||
private void UpdateControls(Color color)
|
||||
{
|
||||
// What in the FUCK
|
||||
|
||||
TextBoxNumeric redBox = FindChildByName("RedBox", false) as TextBoxNumeric;
|
||||
if (redBox != null)
|
||||
redBox.SetText(color.R.ToString(), false);
|
||||
|
||||
TextBoxNumeric greenBox = FindChildByName("GreenBox", false) as TextBoxNumeric;
|
||||
if (greenBox != null)
|
||||
greenBox.SetText(color.G.ToString(), false);
|
||||
|
||||
TextBoxNumeric blueBox = FindChildByName("BlueBox", false) as TextBoxNumeric;
|
||||
if (blueBox != null)
|
||||
blueBox.SetText(color.B.ToString(), false);
|
||||
|
||||
m_After.Color = color;
|
||||
|
||||
if (ColorChanged != null)
|
||||
ColorChanged.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the selected color.
|
||||
/// </summary>
|
||||
/// <param name="color">Color to set.</param>
|
||||
/// <param name="onlyHue">Determines whether only the hue should be set.</param>
|
||||
/// <param name="reset">Determines whether the "before" color should be set as well.</param>
|
||||
public void SetColor(Color color, bool onlyHue = false, bool reset = false)
|
||||
{
|
||||
UpdateControls(color);
|
||||
|
||||
if (reset)
|
||||
m_Before.Color = color;
|
||||
|
||||
m_ColorSlider.SelectedColor = color;
|
||||
m_LerpBox.SetColor(color, onlyHue);
|
||||
m_After.Color = color;
|
||||
}
|
||||
|
||||
private void ColorBoxChanged(Base control)
|
||||
{
|
||||
UpdateControls(SelectedColor);
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
private void ColorSliderChanged(Base control)
|
||||
{
|
||||
if (m_LerpBox != null)
|
||||
m_LerpBox.SetColor(m_ColorSlider.SelectedColor, true);
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
203
Gwen/Control/HorizontalScrollBar.cs
Normal file
203
Gwen/Control/HorizontalScrollBar.cs
Normal file
|
@ -0,0 +1,203 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.Input;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Horizontal scrollbar.
|
||||
/// </summary>
|
||||
public class HorizontalScrollBar : ScrollBar
|
||||
{
|
||||
/// <summary>
|
||||
/// Bar size (in pixels).
|
||||
/// </summary>
|
||||
public override int BarSize
|
||||
{
|
||||
get { return m_Bar.Width; }
|
||||
set { m_Bar.Width = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bar position (in pixels).
|
||||
/// </summary>
|
||||
public override int BarPos
|
||||
{
|
||||
get { return m_Bar.X - Height; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the bar is horizontal.
|
||||
/// </summary>
|
||||
public override bool IsHorizontal
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Button size (in pixels).
|
||||
/// </summary>
|
||||
public override int ButtonSize
|
||||
{
|
||||
get { return Height; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HorizontalScrollBar"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public HorizontalScrollBar(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_Bar.IsHorizontal = true;
|
||||
|
||||
m_ScrollButton[0].SetDirectionLeft();
|
||||
m_ScrollButton[0].Clicked += NudgeLeft;
|
||||
|
||||
m_ScrollButton[1].SetDirectionRight();
|
||||
m_ScrollButton[1].Clicked += NudgeRight;
|
||||
|
||||
m_Bar.Dragged += OnBarMoved;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
base.Layout(skin);
|
||||
|
||||
m_ScrollButton[0].Width = Height;
|
||||
m_ScrollButton[0].Dock = Pos.Left;
|
||||
|
||||
m_ScrollButton[1].Width = Height;
|
||||
m_ScrollButton[1].Dock = Pos.Right;
|
||||
|
||||
m_Bar.Height = ButtonSize;
|
||||
m_Bar.Padding = new Padding(ButtonSize, 0, ButtonSize, 0);
|
||||
|
||||
float barWidth = (m_ViewableContentSize / m_ContentSize) * (Width - (ButtonSize * 2));
|
||||
|
||||
if (barWidth < ButtonSize * 0.5f)
|
||||
barWidth = (int)(ButtonSize * 0.5f);
|
||||
|
||||
m_Bar.Width = (int)(barWidth);
|
||||
m_Bar.IsHidden = Width - (ButtonSize * 2) <= barWidth;
|
||||
|
||||
//Based on our last scroll amount, produce a position for the bar
|
||||
if (!m_Bar.IsHeld)
|
||||
{
|
||||
SetScrollAmount(ScrollAmount, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void NudgeLeft(Base control)
|
||||
{
|
||||
if (!IsDisabled)
|
||||
SetScrollAmount(ScrollAmount - NudgeAmount, true);
|
||||
}
|
||||
|
||||
public void NudgeRight(Base control)
|
||||
{
|
||||
if (!IsDisabled)
|
||||
SetScrollAmount(ScrollAmount + NudgeAmount, true);
|
||||
}
|
||||
|
||||
public override void ScrollToLeft()
|
||||
{
|
||||
SetScrollAmount(0, true);
|
||||
}
|
||||
|
||||
public override void ScrollToRight()
|
||||
{
|
||||
SetScrollAmount(1, true);
|
||||
}
|
||||
|
||||
public override float NudgeAmount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Depressed)
|
||||
return m_ViewableContentSize / m_ContentSize;
|
||||
else
|
||||
return base.NudgeAmount;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.NudgeAmount = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="down">If set to <c>true</c> mouse button is down.</param>
|
||||
protected override void OnMouseClickedLeft(int x, int y, bool down)
|
||||
{
|
||||
if (down)
|
||||
{
|
||||
m_Depressed = true;
|
||||
InputHandler.MouseFocus = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
Point clickPos = CanvasPosToLocal(new Point(x, y));
|
||||
if (clickPos.X < m_Bar.X)
|
||||
NudgeLeft(this);
|
||||
else
|
||||
if (clickPos.X > m_Bar.X + m_Bar.Width)
|
||||
NudgeRight(this);
|
||||
|
||||
m_Depressed = false;
|
||||
InputHandler.MouseFocus = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override float CalculateScrolledAmount()
|
||||
{
|
||||
return (float)(m_Bar.X - ButtonSize) / (Width - m_Bar.Width - (ButtonSize * 2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the scroll amount (0-1).
|
||||
/// </summary>
|
||||
/// <param name="value">Scroll amount.</param>
|
||||
/// <param name="forceUpdate">Determines whether the control should be updated.</param>
|
||||
/// <returns>
|
||||
/// True if control state changed.
|
||||
/// </returns>
|
||||
public override bool SetScrollAmount(float value, bool forceUpdate = false)
|
||||
{
|
||||
value = Util.Clamp(value, 0, 1);
|
||||
|
||||
if (!base.SetScrollAmount(value, forceUpdate))
|
||||
return false;
|
||||
|
||||
if (forceUpdate)
|
||||
{
|
||||
int newX = (int)(ButtonSize + (value * ((Width - m_Bar.Width) - (ButtonSize * 2))));
|
||||
m_Bar.MoveTo(newX, m_Bar.Y);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the BarMoved event.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source.</param>
|
||||
protected override void OnBarMoved(Base control)
|
||||
{
|
||||
if (m_Bar.IsHeld)
|
||||
{
|
||||
SetScrollAmount(CalculateScrolledAmount(), false);
|
||||
base.OnBarMoved(control);
|
||||
}
|
||||
else
|
||||
InvalidateParent();
|
||||
}
|
||||
}
|
||||
}
|
63
Gwen/Control/HorizontalSlider.cs
Normal file
63
Gwen/Control/HorizontalSlider.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Horizontal slider.
|
||||
/// </summary>
|
||||
public class HorizontalSlider : Slider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HorizontalSlider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public HorizontalSlider(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_SliderBar.IsHorizontal = true;
|
||||
}
|
||||
|
||||
protected override float CalculateValue()
|
||||
{
|
||||
return (float)m_SliderBar.X / (Width - m_SliderBar.Width);
|
||||
}
|
||||
|
||||
protected override void UpdateBarFromValue()
|
||||
{
|
||||
m_SliderBar.MoveTo((int)((Width - m_SliderBar.Width) * (m_Value)), m_SliderBar.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="down">If set to <c>true</c> mouse button is down.</param>
|
||||
protected override void OnMouseClickedLeft(int x, int y, bool down)
|
||||
{
|
||||
m_SliderBar.MoveTo((int)(CanvasPosToLocal(new Point(x, y)).X - m_SliderBar.Width*0.5), m_SliderBar.Y);
|
||||
m_SliderBar.InputMouseClickedLeft(x, y, down);
|
||||
OnMoved(m_SliderBar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
m_SliderBar.SetSize(15, Height);
|
||||
UpdateBarFromValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawSlider(this, true, m_SnapToNotches ? m_NotchCount : 0, m_SliderBar.Width);
|
||||
}
|
||||
}
|
||||
}
|
215
Gwen/Control/HorizontalSplitter.cs
Normal file
215
Gwen/Control/HorizontalSplitter.cs
Normal file
|
@ -0,0 +1,215 @@
|
|||
using System;
|
||||
//using System.Windows.Forms;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
public class HorizontalSplitter : Base
|
||||
{
|
||||
private readonly SplitterBar m_VSplitter;
|
||||
private readonly Base[] m_Sections;
|
||||
|
||||
private float m_VVal; // 0-1
|
||||
private int m_BarSize; // pixels
|
||||
private int m_ZoomedSection; // 0-1
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when one of the panels has been zoomed (maximized).
|
||||
/// </summary>
|
||||
public event GwenEventHandler PanelZoomed;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when one of the panels has been unzoomed (restored).
|
||||
/// </summary>
|
||||
public event GwenEventHandler PanelUnZoomed;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the zoomed panel has been changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ZoomChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CrossSplitter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public HorizontalSplitter(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_Sections = new Base[2];
|
||||
|
||||
m_VSplitter = new SplitterBar(this);
|
||||
m_VSplitter.SetPosition(0, 128);
|
||||
m_VSplitter.Dragged += OnVerticalMoved;
|
||||
//m_VSplitter.Cursor = Cursors.SizeNS;
|
||||
|
||||
m_VVal = 0.5f;
|
||||
|
||||
SetPanel(0, null);
|
||||
SetPanel(1, null);
|
||||
|
||||
SplitterSize = 5;
|
||||
SplittersVisible = false;
|
||||
|
||||
m_ZoomedSection = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Centers the panels so that they take even amount of space.
|
||||
/// </summary>
|
||||
public void CenterPanels()
|
||||
{
|
||||
m_VVal = 0.5f;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether any of the panels is zoomed.
|
||||
/// </summary>
|
||||
public bool IsZoomed { get { return m_ZoomedSection != -1; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether splitters should be visible.
|
||||
/// </summary>
|
||||
public bool SplittersVisible
|
||||
{
|
||||
get { return m_VSplitter.ShouldDrawBackground; }
|
||||
set
|
||||
{
|
||||
m_VSplitter.ShouldDrawBackground = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the splitter.
|
||||
/// </summary>
|
||||
public int SplitterSize { get { return m_BarSize; } set { m_BarSize = value; } }
|
||||
|
||||
private void UpdateVSplitter()
|
||||
{
|
||||
m_VSplitter.MoveTo(m_VSplitter.X, (Height - m_VSplitter.Height) * (m_VVal));
|
||||
}
|
||||
|
||||
protected void OnVerticalMoved(Base control)
|
||||
{
|
||||
m_VVal = CalculateValueVertical();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
private float CalculateValueVertical()
|
||||
{
|
||||
return m_VSplitter.Y / (float)(Height - m_VSplitter.Height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
m_VSplitter.SetSize(Width, m_BarSize);
|
||||
|
||||
UpdateVSplitter();
|
||||
|
||||
if (m_ZoomedSection == -1)
|
||||
{
|
||||
if (m_Sections[0] != null)
|
||||
m_Sections[0].SetBounds(0, 0, Width, m_VSplitter.Y);
|
||||
|
||||
if (m_Sections[1] != null)
|
||||
m_Sections[1].SetBounds(0, m_VSplitter.Y + m_BarSize, Width, Height - (m_VSplitter.Y + m_BarSize));
|
||||
}
|
||||
else
|
||||
{
|
||||
//This should probably use Fill docking instead
|
||||
m_Sections[m_ZoomedSection].SetBounds(0, 0, Width, Height);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a control to the specific inner section.
|
||||
/// </summary>
|
||||
/// <param name="index">Section index (0-3).</param>
|
||||
/// <param name="panel">Control to assign.</param>
|
||||
public void SetPanel(int index, Base panel)
|
||||
{
|
||||
m_Sections[index] = panel;
|
||||
|
||||
if (panel != null)
|
||||
{
|
||||
panel.Dock = Pos.None;
|
||||
panel.Parent = this;
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specific inner section.
|
||||
/// </summary>
|
||||
/// <param name="index">Section index (0-3).</param>
|
||||
/// <returns>Specified section.</returns>
|
||||
public Base GetPanel(int index)
|
||||
{
|
||||
return m_Sections[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal handler for the zoom changed event.
|
||||
/// </summary>
|
||||
protected void OnZoomChanged()
|
||||
{
|
||||
if (ZoomChanged != null)
|
||||
ZoomChanged.Invoke(this);
|
||||
|
||||
if (m_ZoomedSection == -1)
|
||||
{
|
||||
if (PanelUnZoomed != null)
|
||||
PanelUnZoomed.Invoke(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PanelZoomed != null)
|
||||
PanelZoomed.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximizes the specified panel so it fills the entire control.
|
||||
/// </summary>
|
||||
/// <param name="section">Panel index (0-3).</param>
|
||||
public void Zoom(int section)
|
||||
{
|
||||
UnZoom();
|
||||
|
||||
if (m_Sections[section] != null)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (i != section && m_Sections[i] != null)
|
||||
m_Sections[i].IsHidden = true;
|
||||
}
|
||||
m_ZoomedSection = section;
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
OnZoomChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores the control so all panels are visible.
|
||||
/// </summary>
|
||||
public void UnZoom()
|
||||
{
|
||||
m_ZoomedSection = -1;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (m_Sections[i] != null)
|
||||
m_Sections[i].IsHidden = false;
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
OnZoomChanged();
|
||||
}
|
||||
}
|
||||
}
|
10
Gwen/Control/IColorPicker.cs
Normal file
10
Gwen/Control/IColorPicker.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
public interface IColorPicker
|
||||
{
|
||||
Color SelectedColor { get; }
|
||||
}
|
||||
}
|
77
Gwen/Control/ImagePanel.cs
Normal file
77
Gwen/Control/ImagePanel.cs
Normal file
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Image container.
|
||||
/// </summary>
|
||||
public class ImagePanel : Base
|
||||
{
|
||||
private readonly Texture m_Texture;
|
||||
private readonly float[] m_uv;
|
||||
private Color m_DrawColor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImagePanel"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public ImagePanel(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_uv = new float[4];
|
||||
m_Texture = new Texture(Skin.Renderer);
|
||||
SetUV(0, 0, 1, 1);
|
||||
MouseInputEnabled = false;
|
||||
m_DrawColor = Color.White;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
m_Texture.Dispose();
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the texture coordinates of the image.
|
||||
/// </summary>
|
||||
public virtual void SetUV(float u1, float v1, float u2, float v2)
|
||||
{
|
||||
m_uv[0] = u1;
|
||||
m_uv[1] = v1;
|
||||
m_uv[2] = u2;
|
||||
m_uv[3] = v2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Texture name.
|
||||
/// </summary>
|
||||
public String ImageName
|
||||
{
|
||||
get { return m_Texture.Name; }
|
||||
set { m_Texture.Load(value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
base.Render(skin);
|
||||
skin.Renderer.DrawColor = m_DrawColor;
|
||||
skin.Renderer.DrawTexturedRect(m_Texture, RenderBounds, m_uv[0], m_uv[1], m_uv[2], m_uv[3]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sizes the control to its contents.
|
||||
/// </summary>
|
||||
public virtual void SizeToContents()
|
||||
{
|
||||
SetSize(m_Texture.Width, m_Texture.Height);
|
||||
}
|
||||
}
|
||||
}
|
211
Gwen/Control/Label.cs
Normal file
211
Gwen/Control/Label.cs
Normal file
|
@ -0,0 +1,211 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Static text label.
|
||||
/// </summary>
|
||||
public class Label : Base
|
||||
{
|
||||
private readonly Text m_Text;
|
||||
private Pos m_Align;
|
||||
private Padding m_TextPadding;
|
||||
private bool m_AutoSizeToContents;
|
||||
|
||||
/// <summary>
|
||||
/// Text alignment.
|
||||
/// </summary>
|
||||
public Pos Alignment { get { return m_Align; } set { m_Align = value; Invalidate(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Text.
|
||||
/// </summary>
|
||||
public String Text { get { return m_Text.String; } set { SetText(value); } }
|
||||
|
||||
/// <summary>
|
||||
/// Font.
|
||||
/// </summary>
|
||||
public Font Font
|
||||
{
|
||||
get { return m_Text.Font; }
|
||||
set
|
||||
{
|
||||
m_Text.Font = value;
|
||||
if (m_AutoSizeToContents)
|
||||
SizeToContents();
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Text color.
|
||||
/// </summary>
|
||||
public Color TextColor { get { return m_Text.TextColor; } set { m_Text.TextColor = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Override text color (used by tooltips).
|
||||
/// </summary>
|
||||
public Color TextColorOverride { get { return m_Text.TextColorOverride; } set { m_Text.TextColorOverride = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Text override - used to display different string.
|
||||
/// </summary>
|
||||
public String TextOverride { get { return m_Text.TextOverride; } set { m_Text.TextOverride = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Width of the text (in pixels).
|
||||
/// </summary>
|
||||
public int TextWidth { get { return m_Text.Width; } }
|
||||
|
||||
/// <summary>
|
||||
/// Height of the text (in pixels).
|
||||
/// </summary>
|
||||
public int TextHeight { get { return m_Text.Height; } }
|
||||
|
||||
public int TextX { get { return m_Text.X; } }
|
||||
public int TextY { get { return m_Text.Y; } }
|
||||
|
||||
/// <summary>
|
||||
/// Text length (in characters).
|
||||
/// </summary>
|
||||
public int TextLength { get { return m_Text.Length; } }
|
||||
public int TextRight { get { return m_Text.Right; } }
|
||||
public virtual void MakeColorNormal() { TextColor = Skin.Colors.Label.Default; }
|
||||
public virtual void MakeColorBright() { TextColor = Skin.Colors.Label.Bright; }
|
||||
public virtual void MakeColorDark() { TextColor = Skin.Colors.Label.Dark; }
|
||||
public virtual void MakeColorHighlight() { TextColor = Skin.Colors.Label.Highlight; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the control should autosize to its text.
|
||||
/// </summary>
|
||||
public bool AutoSizeToContents { get { return m_AutoSizeToContents; } set { m_AutoSizeToContents = value; Invalidate(); InvalidateParent(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Text padding.
|
||||
/// </summary>
|
||||
public Padding TextPadding { get { return m_TextPadding; } set { m_TextPadding = value; Invalidate(); InvalidateParent(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Label"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Label(Base parent) : base(parent)
|
||||
{
|
||||
m_Text = new Text(this);
|
||||
//m_Text.Font = Skin.DefaultFont;
|
||||
|
||||
MouseInputEnabled = false;
|
||||
SetSize(100, 10);
|
||||
Alignment = Pos.Left | Pos.Top;
|
||||
|
||||
m_AutoSizeToContents = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns index of the character closest to specified point (in canvas coordinates).
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns></returns>
|
||||
protected int GetClosestCharacter(int x, int y)
|
||||
{
|
||||
return m_Text.GetClosestCharacter(m_Text.CanvasPosToLocal(new Point(x, y)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the position of the internal text control.
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
protected void SetTextPosition(int x, int y)
|
||||
{
|
||||
m_Text.SetPosition(x, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for text changed event.
|
||||
/// </summary>
|
||||
protected virtual void OnTextChanged()
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
base.Layout(skin);
|
||||
|
||||
Pos align = m_Align;
|
||||
|
||||
if (m_AutoSizeToContents)
|
||||
SizeToContents();
|
||||
|
||||
int x = m_TextPadding.Left + Padding.Left;
|
||||
int y = m_TextPadding.Top + Padding.Top;
|
||||
|
||||
if (0 != (align & Pos.Right))
|
||||
x = Width - m_Text.Width - m_TextPadding.Right - Padding.Right;
|
||||
if (0 != (align & Pos.CenterH))
|
||||
x = (int)((m_TextPadding.Left + Padding.Left) + ((Width - m_Text.Width - m_TextPadding.Left - Padding.Left - m_TextPadding.Right - Padding.Right) * 0.5f));
|
||||
|
||||
if (0 != (align & Pos.CenterV))
|
||||
y = (int)((m_TextPadding.Top + Padding.Top) + ((Height - m_Text.Height) * 0.5f) - m_TextPadding.Bottom - Padding.Bottom);
|
||||
if (0 != (align & Pos.Bottom))
|
||||
y = Height - m_Text.Height - m_TextPadding.Bottom - Padding.Bottom;
|
||||
|
||||
m_Text.SetPosition(x, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the label text.
|
||||
/// </summary>
|
||||
/// <param name="str">Text to set.</param>
|
||||
/// <param name="doEvents">Determines whether to invoke "text changed" event.</param>
|
||||
public virtual void SetText(String str, bool doEvents = true)
|
||||
{
|
||||
if (Text == str)
|
||||
return;
|
||||
|
||||
m_Text.String = str;
|
||||
if (m_AutoSizeToContents)
|
||||
SizeToContents();
|
||||
Invalidate();
|
||||
InvalidateParent();
|
||||
|
||||
if (doEvents)
|
||||
OnTextChanged();
|
||||
}
|
||||
|
||||
public virtual void SizeToContents()
|
||||
{
|
||||
m_Text.SetPosition(m_TextPadding.Left + Padding.Left, m_TextPadding.Top + Padding.Top);
|
||||
m_Text.SizeToContents();
|
||||
|
||||
SetSize(m_Text.Width + Padding.Left + Padding.Right + m_TextPadding.Left + m_TextPadding.Right,
|
||||
m_Text.Height + Padding.Top + Padding.Bottom + m_TextPadding.Top + m_TextPadding.Bottom);
|
||||
InvalidateParent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the coordinates of specified character.
|
||||
/// </summary>
|
||||
/// <param name="index">Character index.</param>
|
||||
/// <returns>Character coordinates (local).</returns>
|
||||
public virtual Point GetCharacterPosition(int index)
|
||||
{
|
||||
Point p = m_Text.GetCharacterPosition(index);
|
||||
return new Point(p.X + m_Text.X, p.Y + m_Text.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
30
Gwen/Control/LabelClickable.cs
Normal file
30
Gwen/Control/LabelClickable.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Clickable label (for checkboxes etc).
|
||||
/// </summary>
|
||||
public class LabelClickable : Button
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LabelClickable"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public LabelClickable(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
IsToggle = false;
|
||||
Alignment = Pos.Left | Pos.CenterV;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
// no button look
|
||||
}
|
||||
}
|
||||
}
|
95
Gwen/Control/LabeledCheckBox.cs
Normal file
95
Gwen/Control/LabeledCheckBox.cs
Normal file
|
@ -0,0 +1,95 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// CheckBox with label.
|
||||
/// </summary>
|
||||
public class LabeledCheckBox : Base
|
||||
{
|
||||
private readonly CheckBox m_CheckBox;
|
||||
private readonly LabelClickable m_Label;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the control has been checked.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Checked;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the control has been unchecked.
|
||||
/// </summary>
|
||||
public event GwenEventHandler UnChecked;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the control's check has been changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler CheckChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the control is checked.
|
||||
/// </summary>
|
||||
public bool IsChecked { get { return m_CheckBox.IsChecked; } set { m_CheckBox.IsChecked = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Label text.
|
||||
/// </summary>
|
||||
public String Text { get { return m_Label.Text; } set { m_Label.Text = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LabeledCheckBox"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public LabeledCheckBox(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetSize(200, 19);
|
||||
m_CheckBox = new CheckBox(this);
|
||||
m_CheckBox.Dock = Pos.Left;
|
||||
m_CheckBox.Margin = new Margin(0, 2, 2, 2);
|
||||
m_CheckBox.IsTabable = false;
|
||||
m_CheckBox.CheckChanged += OnCheckChanged;
|
||||
|
||||
m_Label = new LabelClickable(this);
|
||||
m_Label.Dock = Pos.Fill;
|
||||
m_Label.Clicked += m_CheckBox.Press;
|
||||
m_Label.IsTabable = false;
|
||||
|
||||
IsTabable = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CheckChanged event.
|
||||
/// </summary>
|
||||
protected virtual void OnCheckChanged(Base control)
|
||||
{
|
||||
if (m_CheckBox.IsChecked)
|
||||
{
|
||||
if (Checked != null)
|
||||
Checked.Invoke(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UnChecked != null)
|
||||
UnChecked.Invoke(this);
|
||||
}
|
||||
|
||||
if (CheckChanged != null)
|
||||
CheckChanged.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Space keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeySpace(bool down)
|
||||
{
|
||||
base.OnKeySpace(down);
|
||||
if (!down)
|
||||
m_CheckBox.IsChecked = !m_CheckBox.IsChecked;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
93
Gwen/Control/LabeledRadioButton.cs
Normal file
93
Gwen/Control/LabeledRadioButton.cs
Normal file
|
@ -0,0 +1,93 @@
|
|||
using System;
|
||||
using Gwen.Input;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// RadioButton with label.
|
||||
/// </summary>
|
||||
public class LabeledRadioButton : Base
|
||||
{
|
||||
private readonly RadioButton m_RadioButton;
|
||||
private readonly LabelClickable m_Label;
|
||||
|
||||
/// <summary>
|
||||
/// Label text.
|
||||
/// </summary>
|
||||
public String Text { get { return m_Label.Text; } set { m_Label.Text = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LabeledRadioButton"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public LabeledRadioButton(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetSize(100, 20);
|
||||
|
||||
m_RadioButton = new RadioButton(this);
|
||||
//m_RadioButton.Dock = Pos.Left; // no docking, it causes resizing
|
||||
//m_RadioButton.Margin = new Margin(0, 2, 2, 2);
|
||||
m_RadioButton.IsTabable = false;
|
||||
m_RadioButton.KeyboardInputEnabled = false;
|
||||
|
||||
m_Label = new LabelClickable(this);
|
||||
m_Label.Alignment = Pos.Bottom | Pos.Left;
|
||||
m_Label.Text = "Radio Button";
|
||||
//m_Label.Dock = Pos.Fill;
|
||||
m_Label.Clicked += m_RadioButton.Press;
|
||||
m_Label.IsTabable = false;
|
||||
m_Label.KeyboardInputEnabled = false;
|
||||
m_Label.AutoSizeToContents = true;
|
||||
}
|
||||
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
// ugly stuff because we don't have anchoring without docking (docking resizes children)
|
||||
if (m_Label.Height > m_RadioButton.Height) // usually radio is smaller than label so it gets repositioned to avoid clipping with negative Y
|
||||
{
|
||||
m_RadioButton.Y = (m_Label.Height - m_RadioButton.Height)/2;
|
||||
}
|
||||
Align.PlaceRightBottom(m_Label, m_RadioButton);
|
||||
SizeToChildren();
|
||||
base.Layout(skin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the focus overlay.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void RenderFocus(Skin.Base skin)
|
||||
{
|
||||
if (InputHandler.KeyboardFocus != this) return;
|
||||
if (!IsTabable) return;
|
||||
|
||||
skin.DrawKeyboardHighlight(this, RenderBounds, 0);
|
||||
}
|
||||
|
||||
// todo: would be nice to remove that
|
||||
internal RadioButton RadioButton { get { return m_RadioButton; } }
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Space keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeySpace(bool down)
|
||||
{
|
||||
if (down)
|
||||
m_RadioButton.IsChecked = !m_RadioButton.IsChecked;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects the radio button.
|
||||
/// </summary>
|
||||
public virtual void Select()
|
||||
{
|
||||
m_RadioButton.IsChecked = true;
|
||||
}
|
||||
}
|
||||
}
|
53
Gwen/Control/Layout/Positioner.cs
Normal file
53
Gwen/Control/Layout/Positioner.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control.Layout
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper control that positions its children in a specific way.
|
||||
/// </summary>
|
||||
public class Positioner : Base
|
||||
{
|
||||
private Pos m_Pos;
|
||||
|
||||
/// <summary>
|
||||
/// Children position.
|
||||
/// </summary>
|
||||
public Pos Pos { get { return m_Pos; } set { m_Pos = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Positioner"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Positioner(Base parent) : base(parent)
|
||||
{
|
||||
Pos = Pos.Left | Pos.Top;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function invoked after layout.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void PostLayout(Skin.Base skin)
|
||||
{
|
||||
foreach (Base child in Children) // ok?
|
||||
{
|
||||
child.Position(m_Pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper class that centers all its children.
|
||||
/// </summary>
|
||||
public class Center : Positioner
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Center"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Center(Base parent) : base(parent)
|
||||
{
|
||||
Pos = Pos.Center;
|
||||
}
|
||||
}
|
||||
}
|
95
Gwen/Control/Layout/Splitter.cs
Normal file
95
Gwen/Control/Layout/Splitter.cs
Normal file
|
@ -0,0 +1,95 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control.Layout
|
||||
{
|
||||
/// <summary>
|
||||
/// Base splitter class.
|
||||
/// </summary>
|
||||
public class Splitter : Base
|
||||
{
|
||||
private readonly Base[] m_Panel;
|
||||
private readonly bool[] m_Scale;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Splitter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Splitter(Base parent) : base(parent)
|
||||
{
|
||||
m_Panel = new Base[2];
|
||||
m_Scale = new bool[2];
|
||||
m_Scale[0] = true;
|
||||
m_Scale[1] = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the contents of a splitter panel.
|
||||
/// </summary>
|
||||
/// <param name="panelIndex">Panel index (0-1).</param>
|
||||
/// <param name="panel">Panel contents.</param>
|
||||
/// <param name="noScale">Determines whether the content is to be scaled.</param>
|
||||
public void SetPanel(int panelIndex, Base panel, bool noScale = false)
|
||||
{
|
||||
if (panelIndex < 0 || panelIndex > 1)
|
||||
throw new ArgumentException("Invalid panel index", "panelIndex");
|
||||
|
||||
m_Panel[panelIndex] = panel;
|
||||
m_Scale[panelIndex] = !noScale;
|
||||
|
||||
if (null != m_Panel[panelIndex])
|
||||
{
|
||||
m_Panel[panelIndex].Parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the contents of a secific panel.
|
||||
/// </summary>
|
||||
/// <param name="panelIndex">Panel index (0-1).</param>
|
||||
/// <returns></returns>
|
||||
Base GetPanel(int panelIndex)
|
||||
{
|
||||
if (panelIndex < 0 || panelIndex > 1)
|
||||
throw new ArgumentException("Invalid panel index", "panelIndex");
|
||||
return m_Panel[panelIndex];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
LayoutVertical(skin);
|
||||
}
|
||||
|
||||
protected virtual void LayoutVertical(Skin.Base skin)
|
||||
{
|
||||
int w = Width;
|
||||
int h = Height;
|
||||
|
||||
if (m_Panel[0] != null)
|
||||
{
|
||||
Margin m = m_Panel[0].Margin;
|
||||
if (m_Scale[0])
|
||||
m_Panel[0].SetBounds(m.Left, m.Top, w - m.Left - m.Right, (h*0.5f) - m.Top - m.Bottom);
|
||||
else
|
||||
m_Panel[0].Position(Pos.Center, 0, (int) (h*-0.25f));
|
||||
}
|
||||
|
||||
if (m_Panel[1] != null)
|
||||
{
|
||||
Margin m = m_Panel[1].Margin;
|
||||
if (m_Scale[1])
|
||||
m_Panel[1].SetBounds(m.Left, m.Top + (h*0.5f), w - m.Left - m.Right, (h*0.5f) - m.Top - m.Bottom);
|
||||
else
|
||||
m_Panel[1].Position(Pos.Center, 0, (int) (h*0.25f));
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void LayoutHorizontal(Skin.Base skin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
247
Gwen/Control/Layout/Table.cs
Normal file
247
Gwen/Control/Layout/Table.cs
Normal file
|
@ -0,0 +1,247 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Gwen.Control.Layout
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for multi-column tables.
|
||||
/// </summary>
|
||||
public class Table : Base
|
||||
{
|
||||
// only children of this control should be TableRow.
|
||||
|
||||
private bool m_SizeToContents;
|
||||
private int m_ColumnCount;
|
||||
private int m_DefaultRowHeight;
|
||||
private int m_MaxWidth; // for autosizing, if nonzero - fills last cell up to this size
|
||||
|
||||
private readonly int[] m_ColumnWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Column count (default 1).
|
||||
/// </summary>
|
||||
public int ColumnCount { get { return m_ColumnCount; } set { SetColumnCount(value); Invalidate(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Row count.
|
||||
/// </summary>
|
||||
public int RowCount { get { return Children.Count; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets default height for new table rows.
|
||||
/// </summary>
|
||||
public int DefaultRowHeight { get { return m_DefaultRowHeight; } set { m_DefaultRowHeight = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns specific row of the table.
|
||||
/// </summary>
|
||||
/// <param name="index">Row index.</param>
|
||||
/// <returns>Row at the specified index.</returns>
|
||||
public TableRow this[int index] { get { return Children[index] as TableRow; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Table"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Table(Base parent) : base(parent)
|
||||
{
|
||||
m_ColumnCount = 1;
|
||||
m_DefaultRowHeight = 22;
|
||||
|
||||
m_ColumnWidth = new int[TableRow.MaxColumns];
|
||||
|
||||
for (int i = 0; i < TableRow.MaxColumns; i++)
|
||||
{
|
||||
m_ColumnWidth[i] = 20;
|
||||
}
|
||||
|
||||
m_SizeToContents = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the number of columns.
|
||||
/// </summary>
|
||||
/// <param name="count">Number of columns.</param>
|
||||
public void SetColumnCount(int count)
|
||||
{
|
||||
if (m_ColumnCount == count) return;
|
||||
foreach (TableRow row in Children.OfType<TableRow>())
|
||||
{
|
||||
row.ColumnCount = count;
|
||||
}
|
||||
|
||||
m_ColumnCount = count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the column width (in pixels).
|
||||
/// </summary>
|
||||
/// <param name="column">Column index.</param>
|
||||
/// <param name="width">Column width.</param>
|
||||
public void SetColumnWidth(int column, int width)
|
||||
{
|
||||
if (m_ColumnWidth[column] == width)
|
||||
return;
|
||||
m_ColumnWidth[column] = width;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the column width (in pixels).
|
||||
/// </summary>
|
||||
/// <param name="column">Column index.</param>
|
||||
/// <returns>Column width.</returns>
|
||||
public int GetColumnWidth(int column)
|
||||
{
|
||||
return m_ColumnWidth[column];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new empty row.
|
||||
/// </summary>
|
||||
/// <returns>Newly created row.</returns>
|
||||
public TableRow AddRow()
|
||||
{
|
||||
TableRow row = new TableRow(this);
|
||||
row.ColumnCount = m_ColumnCount;
|
||||
row.Height = m_DefaultRowHeight;
|
||||
row.Dock = Pos.Top;
|
||||
return row;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new row.
|
||||
/// </summary>
|
||||
/// <param name="row">Row to add.</param>
|
||||
public void AddRow(TableRow row)
|
||||
{
|
||||
row.Parent = this;
|
||||
row.ColumnCount = m_ColumnCount;
|
||||
row.Height = m_DefaultRowHeight;
|
||||
row.Dock = Pos.Top;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new row with specified text in first column.
|
||||
/// </summary>
|
||||
/// <param name="text">Text to add.</param>
|
||||
/// <returns>New row.</returns>
|
||||
public TableRow AddRow(String text)
|
||||
{
|
||||
var row = AddRow();
|
||||
row.SetCellText(0, text);
|
||||
return row;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a row by reference.
|
||||
/// </summary>
|
||||
/// <param name="row">Row to remove.</param>
|
||||
public void RemoveRow(TableRow row)
|
||||
{
|
||||
RemoveChild(row, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a row by index.
|
||||
/// </summary>
|
||||
/// <param name="idx">Row index.</param>
|
||||
public void RemoveRow(int idx)
|
||||
{
|
||||
var row = Children[idx];
|
||||
RemoveRow(row as TableRow);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all rows.
|
||||
/// </summary>
|
||||
public void RemoveAll()
|
||||
{
|
||||
while (RowCount > 0)
|
||||
RemoveRow(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of a specified row.
|
||||
/// </summary>
|
||||
/// <param name="row">Row to search for.</param>
|
||||
/// <returns>Row index if found, -1 otherwise.</returns>
|
||||
public int GetRowIndex(TableRow row)
|
||||
{
|
||||
return Children.IndexOf(row);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
base.Layout(skin);
|
||||
|
||||
bool even = false;
|
||||
foreach (TableRow row in Children)
|
||||
{
|
||||
row.EvenRow = even;
|
||||
even = !even;
|
||||
for (int i = 0; i < m_ColumnCount; i++)
|
||||
{
|
||||
row.SetColumnWidth(i, m_ColumnWidth[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PostLayout(Skin.Base skin)
|
||||
{
|
||||
base.PostLayout(skin);
|
||||
if (m_SizeToContents)
|
||||
{
|
||||
DoSizeToContents();
|
||||
m_SizeToContents = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sizes to fit contents.
|
||||
/// </summary>
|
||||
public void SizeToContents(int maxWidth)
|
||||
{
|
||||
m_MaxWidth = maxWidth;
|
||||
m_SizeToContents = true;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected void DoSizeToContents()
|
||||
{
|
||||
int height = 0;
|
||||
int width = 0;
|
||||
|
||||
foreach (TableRow row in Children)
|
||||
{
|
||||
row.SizeToContents(); // now all columns fit but only in this particular row
|
||||
|
||||
for (int i = 0; i < ColumnCount; i++)
|
||||
{
|
||||
Base cell = row.GetColumn(i);
|
||||
if (null != cell)
|
||||
{
|
||||
if (i < ColumnCount - 1 || m_MaxWidth == 0)
|
||||
m_ColumnWidth[i] = Math.Max(m_ColumnWidth[i], cell.Width + cell.Margin.Left + cell.Margin.Right);
|
||||
else
|
||||
m_ColumnWidth[i] = m_MaxWidth - width; // last cell - fill
|
||||
}
|
||||
}
|
||||
height += row.Height;
|
||||
}
|
||||
|
||||
// sum all column widths
|
||||
for (int i = 0; i < ColumnCount; i++)
|
||||
{
|
||||
width += m_ColumnWidth[i];
|
||||
}
|
||||
|
||||
SetSize(width, height);
|
||||
//InvalidateParent();
|
||||
}
|
||||
}
|
||||
}
|
221
Gwen/Control/Layout/TableRow.cs
Normal file
221
Gwen/Control/Layout/TableRow.cs
Normal file
|
@ -0,0 +1,221 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Gwen.Control.Layout
|
||||
{
|
||||
/// <summary>
|
||||
/// Single table row.
|
||||
/// </summary>
|
||||
public class TableRow : Base
|
||||
{
|
||||
// [omeg] todo: get rid of this
|
||||
public const int MaxColumns = 5;
|
||||
|
||||
private int m_ColumnCount;
|
||||
private bool m_EvenRow;
|
||||
private readonly Label[] m_Columns;
|
||||
|
||||
internal Label GetColumn(int index)
|
||||
{
|
||||
return m_Columns[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the row has been selected.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Selected;
|
||||
|
||||
/// <summary>
|
||||
/// Column count.
|
||||
/// </summary>
|
||||
public int ColumnCount { get { return m_ColumnCount; } set { SetColumnCount(value); } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the row is even or odd (used for alternate coloring).
|
||||
/// </summary>
|
||||
public bool EvenRow { get { return m_EvenRow; } set { m_EvenRow = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Text of the first column.
|
||||
/// </summary>
|
||||
public String Text { get { return GetText(0); } set { SetCellText(0, value); } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TableRow"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public TableRow(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_Columns = new Label[MaxColumns];
|
||||
m_ColumnCount = 0;
|
||||
KeyboardInputEnabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the number of columns.
|
||||
/// </summary>
|
||||
/// <param name="columnCount">Number of columns.</param>
|
||||
protected void SetColumnCount(int columnCount)
|
||||
{
|
||||
if (columnCount == m_ColumnCount) return;
|
||||
|
||||
if (columnCount >= MaxColumns)
|
||||
throw new ArgumentException("Invalid column count", "columnCount");
|
||||
|
||||
for (int i = 0; i < MaxColumns; i++)
|
||||
{
|
||||
if (i < columnCount)
|
||||
{
|
||||
if (null == m_Columns[i])
|
||||
{
|
||||
m_Columns[i] = new Label(this);
|
||||
m_Columns[i].Padding = Padding.Three;
|
||||
m_Columns[i].Margin = new Margin(0, 0, 2, 0); // to separate them slightly
|
||||
if (i == columnCount - 1)
|
||||
{
|
||||
// last column fills remaining space
|
||||
m_Columns[i].Dock = Pos.Fill;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Columns[i].Dock = Pos.Left;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (null != m_Columns[i])
|
||||
{
|
||||
RemoveChild(m_Columns[i], true);
|
||||
m_Columns[i] = null;
|
||||
}
|
||||
|
||||
m_ColumnCount = columnCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the column width (in pixels).
|
||||
/// </summary>
|
||||
/// <param name="column">Column index.</param>
|
||||
/// <param name="width">Column width.</param>
|
||||
public void SetColumnWidth(int column, int width)
|
||||
{
|
||||
if (null == m_Columns[column])
|
||||
return;
|
||||
if (m_Columns[column].Width == width)
|
||||
return;
|
||||
|
||||
m_Columns[column].Width = width;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the text of a specified cell.
|
||||
/// </summary>
|
||||
/// <param name="column">Column number.</param>
|
||||
/// <param name="text">Text to set.</param>
|
||||
public void SetCellText(int column, String text)
|
||||
{
|
||||
if (null == m_Columns[column])
|
||||
return;
|
||||
|
||||
m_Columns[column].Text = text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the contents of a specified cell.
|
||||
/// </summary>
|
||||
/// <param name="column">Column number.</param>
|
||||
/// <param name="control">Cell contents.</param>
|
||||
/// <param name="enableMouseInput">Determines whether mouse input should be enabled for the cell.</param>
|
||||
public void SetCellContents(int column, Base control, bool enableMouseInput = false)
|
||||
{
|
||||
if (null == m_Columns[column])
|
||||
return;
|
||||
|
||||
control.Parent = m_Columns[column];
|
||||
m_Columns[column].MouseInputEnabled = enableMouseInput;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the contents of a specified cell.
|
||||
/// </summary>
|
||||
/// <param name="column">Column number.</param>
|
||||
/// <returns>Control embedded in the cell.</returns>
|
||||
public Base GetCellContents(int column)
|
||||
{
|
||||
return m_Columns[column];
|
||||
}
|
||||
|
||||
protected virtual void OnRowSelected()
|
||||
{
|
||||
if (Selected != null)
|
||||
Selected.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sizes all cells to fit contents.
|
||||
/// </summary>
|
||||
public void SizeToContents()
|
||||
{
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
for (int i = 0; i < m_ColumnCount; i++)
|
||||
{
|
||||
if (null == m_Columns[i])
|
||||
continue;
|
||||
|
||||
// Note, more than 1 child here, because the
|
||||
// label has a child built in ( The Text )
|
||||
if (m_Columns[i].Children.Count > 1)
|
||||
{
|
||||
m_Columns[i].SizeToChildren();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Columns[i].SizeToContents();
|
||||
}
|
||||
|
||||
//if (i == m_ColumnCount - 1) // last column
|
||||
// m_Columns[i].Width = Parent.Width - width; // fill if not autosized
|
||||
|
||||
width += m_Columns[i].Width + m_Columns[i].Margin.Left + m_Columns[i].Margin.Right;
|
||||
height = Math.Max(height, m_Columns[i].Height + m_Columns[i].Margin.Top + m_Columns[i].Margin.Bottom);
|
||||
}
|
||||
|
||||
SetSize(width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the text color for all cells.
|
||||
/// </summary>
|
||||
/// <param name="color">Text color.</param>
|
||||
public void SetTextColor(Color color)
|
||||
{
|
||||
for (int i = 0; i < m_ColumnCount; i++)
|
||||
{
|
||||
if (null == m_Columns[i]) continue;
|
||||
m_Columns[i].TextColor = color;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns text of a specified row cell (default first).
|
||||
/// </summary>
|
||||
/// <param name="column">Column index.</param>
|
||||
/// <returns>Column cell text.</returns>
|
||||
public String GetText(int column = 0)
|
||||
{
|
||||
return m_Columns[column].Text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Copy event.
|
||||
/// </summary>
|
||||
/// <param name="from">Source control.</param>
|
||||
protected override void OnCopy(Base from)
|
||||
{
|
||||
Platform.Neutral.SetClipboardText(Text);
|
||||
}
|
||||
}
|
||||
}
|
325
Gwen/Control/ListBox.cs
Normal file
325
Gwen/Control/ListBox.cs
Normal file
|
@ -0,0 +1,325 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Gwen.Control.Layout;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// ListBox control.
|
||||
/// </summary>
|
||||
public class ListBox : ScrollControl
|
||||
{
|
||||
private readonly Table m_Table;
|
||||
private readonly List<TableRow> m_SelectedRows;
|
||||
|
||||
private bool m_MultiSelect;
|
||||
private bool m_IsToggle;
|
||||
private bool m_SizeToContents;
|
||||
private Pos m_OldDock; // used while autosizing
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether multiple rows can be selected at once.
|
||||
/// </summary>
|
||||
public bool AllowMultiSelect
|
||||
{
|
||||
get { return m_MultiSelect; }
|
||||
set
|
||||
{
|
||||
m_MultiSelect = value;
|
||||
if (value)
|
||||
IsToggle = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether rows can be unselected by clicking on them again.
|
||||
/// </summary>
|
||||
public bool IsToggle { get { return m_IsToggle; } set { m_IsToggle = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Number of rows in the list box.
|
||||
/// </summary>
|
||||
public int RowCount { get { return m_Table.RowCount; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns specific row of the ListBox.
|
||||
/// </summary>
|
||||
/// <param name="index">Row index.</param>
|
||||
/// <returns>Row at the specified index.</returns>
|
||||
public ListBoxRow this[int index] { get { return m_Table[index] as ListBoxRow; } }
|
||||
|
||||
/// <summary>
|
||||
/// List of selected rows.
|
||||
/// </summary>
|
||||
public IEnumerable<TableRow> SelectedRows { get { return m_SelectedRows; } }
|
||||
|
||||
/// <summary>
|
||||
/// First selected row (and only if list is not multiselectable).
|
||||
/// </summary>
|
||||
public TableRow SelectedRow
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_SelectedRows.Count == 0)
|
||||
return null;
|
||||
return m_SelectedRows[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected row number.
|
||||
/// </summary>
|
||||
public int SelectedRowIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
var selected = SelectedRow;
|
||||
if (selected == null)
|
||||
return -1;
|
||||
return m_Table.GetRowIndex(selected);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Column count of table rows.
|
||||
/// </summary>
|
||||
public int ColumnCount { get { return m_Table.ColumnCount; } set { m_Table.ColumnCount = value; Invalidate(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a row has been selected.
|
||||
/// </summary>
|
||||
public event GwenEventHandler RowSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked whan a row has beed unselected.
|
||||
/// </summary>
|
||||
public event GwenEventHandler RowUnselected;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ListBox"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public ListBox(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_SelectedRows = new List<TableRow>();
|
||||
|
||||
EnableScroll(false, true);
|
||||
AutoHideBars = true;
|
||||
Margin = Margin.One;
|
||||
|
||||
m_Table = new Table(this);
|
||||
m_Table.Dock = Pos.Fill;
|
||||
m_Table.ColumnCount = 1;
|
||||
m_Table.BoundsChanged += TableResized;
|
||||
|
||||
m_MultiSelect = false;
|
||||
m_IsToggle = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects the specified row by index.
|
||||
/// </summary>
|
||||
/// <param name="index">Row to select.</param>
|
||||
/// <param name="clearOthers">Determines whether to deselect previously selected rows.</param>
|
||||
public void SelectRow(int index, bool clearOthers = false)
|
||||
{
|
||||
if (index < 0 || index >= m_Table.RowCount)
|
||||
return;
|
||||
|
||||
SelectRow(m_Table.Children[index], clearOthers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects the specified row(s) by text.
|
||||
/// </summary>
|
||||
/// <param name="rowText">Text to search for (exact match).</param>
|
||||
/// <param name="clearOthers">Determines whether to deselect previously selected rows.</param>
|
||||
public void SelectRows(String rowText, bool clearOthers = false)
|
||||
{
|
||||
var rows = m_Table.Children.OfType<ListBoxRow>().Where(x => x.Text == rowText);
|
||||
foreach (ListBoxRow row in rows)
|
||||
{
|
||||
SelectRow(row, clearOthers);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects the specified row(s) by regex text search.
|
||||
/// </summary>
|
||||
/// <param name="pattern">Regex pattern to search for.</param>
|
||||
/// <param name="regexOptions">Regex options.</param>
|
||||
/// <param name="clearOthers">Determines whether to deselect previously selected rows.</param>
|
||||
public void SelectRowsByRegex(String pattern, RegexOptions regexOptions = RegexOptions.None, bool clearOthers = false)
|
||||
{
|
||||
var rows = m_Table.Children.OfType<ListBoxRow>().Where(x => Regex.IsMatch(x.Text, pattern) );
|
||||
foreach (ListBoxRow row in rows)
|
||||
{
|
||||
SelectRow(row, clearOthers);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slelects the specified row.
|
||||
/// </summary>
|
||||
/// <param name="control">Row to select.</param>
|
||||
/// <param name="clearOthers">Determines whether to deselect previously selected rows.</param>
|
||||
public void SelectRow(Base control, bool clearOthers = false)
|
||||
{
|
||||
if (!AllowMultiSelect || clearOthers)
|
||||
UnselectAll();
|
||||
|
||||
ListBoxRow row = control as ListBoxRow;
|
||||
if (row == null)
|
||||
return;
|
||||
|
||||
// TODO: make sure this is one of our rows!
|
||||
row.IsSelected = true;
|
||||
m_SelectedRows.Add(row);
|
||||
if (RowSelected != null)
|
||||
RowSelected.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified row by index.
|
||||
/// </summary>
|
||||
/// <param name="idx">Row index.</param>
|
||||
public void RemoveRow(int idx)
|
||||
{
|
||||
m_Table.RemoveRow(idx); // this calls Dispose()
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new row.
|
||||
/// </summary>
|
||||
/// <param name="label">Row text.</param>
|
||||
/// <returns>Newly created control.</returns>
|
||||
public TableRow AddRow(String label)
|
||||
{
|
||||
return AddRow(label, String.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new row.
|
||||
/// </summary>
|
||||
/// <param name="label">Row text.</param>
|
||||
/// <param name="name">Internal control name.</param>
|
||||
/// <returns>Newly created control.</returns>
|
||||
public TableRow AddRow(String label, String name)
|
||||
{
|
||||
ListBoxRow row = new ListBoxRow(this);
|
||||
m_Table.AddRow(row);
|
||||
|
||||
row.SetCellText(0, label);
|
||||
row.Name = name;
|
||||
|
||||
row.Selected += OnRowSelected;
|
||||
|
||||
m_Table.SizeToContents(Width);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the column width (in pixels).
|
||||
/// </summary>
|
||||
/// <param name="column">Column index.</param>
|
||||
/// <param name="width">Column width.</param>
|
||||
public void SetColumnWidth(int column, int width)
|
||||
{
|
||||
m_Table.SetColumnWidth(column, width);
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawListBox(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deselects all rows.
|
||||
/// </summary>
|
||||
public virtual void UnselectAll()
|
||||
{
|
||||
foreach (ListBoxRow row in m_SelectedRows)
|
||||
{
|
||||
row.IsSelected = false;
|
||||
if (RowUnselected != null)
|
||||
RowUnselected.Invoke(this);
|
||||
}
|
||||
m_SelectedRows.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unselects the specified row.
|
||||
/// </summary>
|
||||
/// <param name="row">Row to unselect.</param>
|
||||
public void UnselectRow(ListBoxRow row)
|
||||
{
|
||||
row.IsSelected = false;
|
||||
m_SelectedRows.Remove(row);
|
||||
|
||||
if (RowUnselected != null)
|
||||
RowUnselected.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the row selection event.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source.</param>
|
||||
protected virtual void OnRowSelected(Base control)
|
||||
{
|
||||
// [omeg] changed default behavior
|
||||
bool clear = false;// !InputHandler.InputHandler.IsShiftDown;
|
||||
ListBoxRow row = control as ListBoxRow;
|
||||
if (row == null)
|
||||
return;
|
||||
|
||||
if (row.IsSelected)
|
||||
{
|
||||
if (IsToggle)
|
||||
UnselectRow(row);
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectRow(control, clear);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all rows.
|
||||
/// </summary>
|
||||
public virtual void Clear()
|
||||
{
|
||||
UnselectAll();
|
||||
m_Table.RemoveAll();
|
||||
}
|
||||
|
||||
public void SizeToContents()
|
||||
{
|
||||
m_SizeToContents = true;
|
||||
// docking interferes with autosizing so we disable it until sizing is done
|
||||
m_OldDock = m_Table.Dock;
|
||||
m_Table.Dock = Pos.None;
|
||||
m_Table.SizeToContents(0); // autosize without constraints
|
||||
}
|
||||
|
||||
private void TableResized(Base control)
|
||||
{
|
||||
if (m_SizeToContents)
|
||||
{
|
||||
SetSize(m_Table.Width, m_Table.Height);
|
||||
m_SizeToContents = false;
|
||||
m_Table.Dock = m_OldDock;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
66
Gwen/Control/ListBoxRow.cs
Normal file
66
Gwen/Control/ListBoxRow.cs
Normal file
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.Control.Layout;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// List box row (selectable).
|
||||
/// </summary>
|
||||
public class ListBoxRow : TableRow
|
||||
{
|
||||
private bool m_Selected;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ListBoxRow"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public ListBoxRow(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
MouseInputEnabled = true;
|
||||
IsSelected = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the control is selected.
|
||||
/// </summary>
|
||||
public bool IsSelected
|
||||
{
|
||||
get { return m_Selected; }
|
||||
set
|
||||
{
|
||||
m_Selected = value;
|
||||
// TODO: Get these values from the skin.
|
||||
if (value)
|
||||
SetTextColor(Color.White);
|
||||
else
|
||||
SetTextColor(Color.Black);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawListBoxLine(this, IsSelected, EvenRow);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="down">If set to <c>true</c> mouse button is down.</param>
|
||||
protected override void OnMouseClickedLeft(int x, int y, bool down)
|
||||
{
|
||||
if (down)
|
||||
{
|
||||
//IsSelected = true; // [omeg] ListBox manages that
|
||||
OnRowSelected();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
209
Gwen/Control/Menu.cs
Normal file
209
Gwen/Control/Menu.cs
Normal file
|
@ -0,0 +1,209 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Popup menu.
|
||||
/// </summary>
|
||||
public class Menu : ScrollControl
|
||||
{
|
||||
private bool m_DisableIconMargin;
|
||||
private bool m_DeleteOnClose;
|
||||
|
||||
internal override bool IsMenuComponent { get { return true; } }
|
||||
|
||||
public bool IconMarginDisabled { get { return m_DisableIconMargin; } set { m_DisableIconMargin = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the menu should be disposed on close.
|
||||
/// </summary>
|
||||
public bool DeleteOnClose { get { return m_DeleteOnClose; } set { m_DeleteOnClose = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the menu should open on mouse hover.
|
||||
/// </summary>
|
||||
protected virtual bool ShouldHoverOpenMenu { get { return true; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Menu"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Menu(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetBounds(0, 0, 10, 10);
|
||||
Padding = Padding.Two;
|
||||
IconMarginDisabled = false;
|
||||
|
||||
AutoHideBars = true;
|
||||
EnableScroll(false, true);
|
||||
DeleteOnClose = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawMenu(this, IconMarginDisabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders under the actual control (shadows etc).
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void RenderUnder(Skin.Base skin)
|
||||
{
|
||||
base.RenderUnder(skin);
|
||||
skin.DrawShadow(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the menu.
|
||||
/// </summary>
|
||||
/// <param name="pos">Unused.</param>
|
||||
public void Open(Pos pos)
|
||||
{
|
||||
IsHidden = false;
|
||||
BringToFront();
|
||||
Point mouse = Input.InputHandler.MousePosition;
|
||||
SetPosition(mouse.X, mouse.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
int childrenHeight = Children.Sum(child => child != null ? child.Height : 0);
|
||||
|
||||
if (Y + childrenHeight > GetCanvas().Height)
|
||||
childrenHeight = GetCanvas().Height - Y;
|
||||
|
||||
SetSize(Width, childrenHeight);
|
||||
|
||||
base.Layout(skin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new menu item.
|
||||
/// </summary>
|
||||
/// <param name="text">Item text.</param>
|
||||
/// <returns>Newly created control.</returns>
|
||||
public virtual MenuItem AddItem(String text)
|
||||
{
|
||||
return AddItem(text, String.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new menu item.
|
||||
/// </summary>
|
||||
/// <param name="text">Item text.</param>
|
||||
/// <param name="iconName">Icon texture name.</param>
|
||||
/// <param name="accelerator">Accelerator for this item.</param>
|
||||
/// <returns>Newly created control.</returns>
|
||||
public virtual MenuItem AddItem(String text, String iconName, String accelerator = "")
|
||||
{
|
||||
MenuItem item = new MenuItem(this);
|
||||
item.Padding = Padding.Four;
|
||||
item.SetText(text);
|
||||
item.SetImage(iconName);
|
||||
item.SetAccelerator(accelerator);
|
||||
|
||||
OnAddItem(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add item handler.
|
||||
/// </summary>
|
||||
/// <param name="item">Item added.</param>
|
||||
protected virtual void OnAddItem(MenuItem item)
|
||||
{
|
||||
item.TextPadding = new Padding(IconMarginDisabled ? 0 : 24, 0, 16, 0);
|
||||
item.Dock = Pos.Top;
|
||||
item.SizeToContents();
|
||||
item.Alignment = Pos.CenterV | Pos.Left;
|
||||
item.HoverEnter += OnHoverItem;
|
||||
|
||||
// Do this here - after Top Docking these values mean nothing in layout
|
||||
int w = item.Width + 10 + 32;
|
||||
if (w < Width) w = Width;
|
||||
SetSize(w, Height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes all submenus.
|
||||
/// </summary>
|
||||
public virtual void CloseAll()
|
||||
{
|
||||
//System.Diagnostics.Debug.Print("Menu.CloseAll: {0}", this);
|
||||
Children.ForEach(child => { if (child is MenuItem) (child as MenuItem).CloseMenu(); });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether any (sub)menu is open.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual bool IsMenuOpen()
|
||||
{
|
||||
return Children.Any(child => { if (child is MenuItem) return (child as MenuItem).IsMenuOpen; return false; });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mouse hover handler.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source.</param>
|
||||
protected virtual void OnHoverItem(Base control)
|
||||
{
|
||||
if (!ShouldHoverOpenMenu) return;
|
||||
|
||||
MenuItem item = control as MenuItem;
|
||||
if (null == item) return;
|
||||
if (item.IsMenuOpen) return;
|
||||
|
||||
CloseAll();
|
||||
item.OpenMenu();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the current menu.
|
||||
/// </summary>
|
||||
public virtual void Close()
|
||||
{
|
||||
//System.Diagnostics.Debug.Print("Menu.Close: {0}", this);
|
||||
IsHidden = true;
|
||||
if (DeleteOnClose)
|
||||
{
|
||||
DelayedDelete();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes all submenus and the current menu.
|
||||
/// </summary>
|
||||
public override void CloseMenus()
|
||||
{
|
||||
//System.Diagnostics.Debug.Print("Menu.CloseMenus: {0}", this);
|
||||
base.CloseMenus();
|
||||
CloseAll();
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a divider menu item.
|
||||
/// </summary>
|
||||
public virtual void AddDivider()
|
||||
{
|
||||
MenuDivider divider = new MenuDivider(this);
|
||||
divider.Dock = Pos.Top;
|
||||
divider.Margin = new Margin(IconMarginDisabled ? 0 : 24, 0, 4, 0);
|
||||
}
|
||||
}
|
||||
}
|
255
Gwen/Control/MenuItem.cs
Normal file
255
Gwen/Control/MenuItem.cs
Normal file
|
@ -0,0 +1,255 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Menu item.
|
||||
/// </summary>
|
||||
public class MenuItem : Button
|
||||
{
|
||||
private bool m_OnStrip;
|
||||
private bool m_Checkable;
|
||||
private bool m_Checked;
|
||||
private Menu m_Menu;
|
||||
private Base m_SubmenuArrow;
|
||||
private Label m_Accelerator;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the item is on a menu strip.
|
||||
/// </summary>
|
||||
public bool IsOnStrip { get { return m_OnStrip; } set { m_OnStrip = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the menu item is checkable.
|
||||
/// </summary>
|
||||
public bool IsCheckable { get { return m_Checkable; } set { m_Checkable = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the parent menu is open.
|
||||
/// </summary>
|
||||
public bool IsMenuOpen { get { if (m_Menu == null) return false; return !m_Menu.IsHidden; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the check value.
|
||||
/// </summary>
|
||||
public bool IsChecked
|
||||
{
|
||||
get { return m_Checked; }
|
||||
set
|
||||
{
|
||||
if (value == m_Checked)
|
||||
return;
|
||||
|
||||
m_Checked = value;
|
||||
|
||||
if (CheckChanged != null)
|
||||
CheckChanged.Invoke(this);
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (Checked != null)
|
||||
Checked.Invoke(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UnChecked != null)
|
||||
UnChecked.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent menu.
|
||||
/// </summary>
|
||||
public Menu Menu
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == m_Menu)
|
||||
{
|
||||
m_Menu = new Menu(GetCanvas());
|
||||
m_Menu.IsHidden = true;
|
||||
|
||||
if (!m_OnStrip)
|
||||
{
|
||||
if (m_SubmenuArrow != null)
|
||||
m_SubmenuArrow.Dispose();
|
||||
m_SubmenuArrow = new RightArrow(this);
|
||||
m_SubmenuArrow.SetSize(15, 15);
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
return m_Menu;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the item is selected.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Selected;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the item is checked.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Checked;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the item is unchecked.
|
||||
/// </summary>
|
||||
public event GwenEventHandler UnChecked;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the item's check value is changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler CheckChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MenuItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public MenuItem(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_OnStrip = false;
|
||||
IsTabable = false;
|
||||
IsCheckable = false;
|
||||
IsChecked = false;
|
||||
|
||||
m_Accelerator = new Label(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawMenuItem(this, IsMenuOpen, m_Checkable ? m_Checked : false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
if (m_SubmenuArrow != null)
|
||||
{
|
||||
m_SubmenuArrow.Position(Pos.Right | Pos.CenterV, 4, 0);
|
||||
}
|
||||
base.Layout(skin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal OnPressed implementation.
|
||||
/// </summary>
|
||||
protected override void OnClicked()
|
||||
{
|
||||
if (m_Menu != null)
|
||||
{
|
||||
ToggleMenu();
|
||||
}
|
||||
else if (!m_OnStrip)
|
||||
{
|
||||
IsChecked = !IsChecked;
|
||||
if (Selected != null)
|
||||
Selected.Invoke(this);
|
||||
GetCanvas().CloseMenus();
|
||||
}
|
||||
base.OnClicked();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the menu open state.
|
||||
/// </summary>
|
||||
public void ToggleMenu()
|
||||
{
|
||||
if (IsMenuOpen)
|
||||
CloseMenu();
|
||||
else
|
||||
OpenMenu();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the menu.
|
||||
/// </summary>
|
||||
public void OpenMenu()
|
||||
{
|
||||
if (null == m_Menu) return;
|
||||
|
||||
m_Menu.IsHidden = false;
|
||||
m_Menu.BringToFront();
|
||||
|
||||
Point p = LocalPosToCanvas(Point.Empty);
|
||||
|
||||
// Strip menus open downwards
|
||||
if (m_OnStrip)
|
||||
{
|
||||
m_Menu.SetPosition(p.X, p.Y + Height + 1);
|
||||
}
|
||||
// Submenus open sidewards
|
||||
else
|
||||
{
|
||||
m_Menu.SetPosition(p.X + Width, p.Y);
|
||||
}
|
||||
|
||||
// TODO: Option this.
|
||||
// TODO: Make sure on screen, open the other side of the
|
||||
// parent if it's better...
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the menu.
|
||||
/// </summary>
|
||||
public void CloseMenu()
|
||||
{
|
||||
if (null == m_Menu) return;
|
||||
m_Menu.Close();
|
||||
m_Menu.CloseAll();
|
||||
}
|
||||
|
||||
public override void SizeToContents()
|
||||
{
|
||||
base.SizeToContents();
|
||||
if (m_Accelerator != null)
|
||||
{
|
||||
m_Accelerator.SizeToContents();
|
||||
Width = Width + m_Accelerator.Width;
|
||||
}
|
||||
}
|
||||
|
||||
public MenuItem SetAction(GwenEventHandler handler)
|
||||
{
|
||||
if (m_Accelerator != null)
|
||||
{
|
||||
AddAccelerator(m_Accelerator.Text, handler);
|
||||
}
|
||||
|
||||
Selected += handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void SetAccelerator(String acc)
|
||||
{
|
||||
if (m_Accelerator != null)
|
||||
{
|
||||
//m_Accelerator.DelayedDelete(); // to prevent double disposing
|
||||
m_Accelerator = null;
|
||||
}
|
||||
|
||||
if (acc == string.Empty)
|
||||
return;
|
||||
|
||||
m_Accelerator = new Label(this);
|
||||
m_Accelerator.Dock = Pos.Right;
|
||||
m_Accelerator.Alignment = Pos.Right | Pos.CenterV;
|
||||
m_Accelerator.Text = acc;
|
||||
m_Accelerator.Margin = new Margin(0, 0, 16, 0);
|
||||
// todo
|
||||
}
|
||||
}
|
||||
}
|
78
Gwen/Control/MenuStrip.cs
Normal file
78
Gwen/Control/MenuStrip.cs
Normal file
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Menu strip.
|
||||
/// </summary>
|
||||
public class MenuStrip : Menu
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MenuStrip"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public MenuStrip(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetBounds(0, 0, 200, 22);
|
||||
Dock = Pos.Top;
|
||||
m_InnerPanel.Padding = new Padding(5, 0, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the current menu.
|
||||
/// </summary>
|
||||
public override void Close()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders under the actual control (shadows etc).
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void RenderUnder(Skin.Base skin)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawMenuStrip(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
//TODO: We don't want to do vertical sizing the same as Menu, do nothing for now
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the menu should open on mouse hover.
|
||||
/// </summary>
|
||||
protected override bool ShouldHoverOpenMenu
|
||||
{
|
||||
get { return IsMenuOpen(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add item handler.
|
||||
/// </summary>
|
||||
/// <param name="item">Item added.</param>
|
||||
protected override void OnAddItem(MenuItem item)
|
||||
{
|
||||
item.Dock = Pos.Left;
|
||||
item.TextPadding = new Padding(5, 0, 5, 0);
|
||||
item.Padding = new Padding(10, 0, 10, 0);
|
||||
item.SizeToContents();
|
||||
item.IsOnStrip = true;
|
||||
item.HoverEnter += OnHoverItem;
|
||||
}
|
||||
}
|
||||
}
|
67
Gwen/Control/MessageBox.cs
Normal file
67
Gwen/Control/MessageBox.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple message box.
|
||||
/// </summary>
|
||||
public class MessageBox : WindowControl
|
||||
{
|
||||
private readonly Button m_Button;
|
||||
private readonly Label m_Label; // should be rich label with maxwidth = parent
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the message box has been dismissed.
|
||||
/// </summary>
|
||||
public GwenEventHandler Dismissed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MessageBox"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
/// <param name="text">Message to display.</param>
|
||||
/// <param name="caption">Window caption.</param>
|
||||
public MessageBox(Base parent, String text, String caption = "")
|
||||
: base(parent, caption, true)
|
||||
{
|
||||
DeleteOnClose = true;
|
||||
|
||||
m_Label = new Label(m_InnerPanel);
|
||||
m_Label.Text = text;
|
||||
m_Label.Margin = Margin.Five;
|
||||
m_Label.Dock = Pos.Top;
|
||||
m_Label.Alignment = Pos.Center;
|
||||
m_Label.AutoSizeToContents = true;
|
||||
|
||||
m_Button = new Button(m_InnerPanel);
|
||||
m_Button.Text = "OK"; // todo: parametrize buttons
|
||||
m_Button.Clicked += CloseButtonPressed;
|
||||
m_Button.Clicked += DismissedHandler;
|
||||
m_Button.Margin = Margin.Five;
|
||||
m_Button.SetSize(50, 20);
|
||||
|
||||
Align.Center(this);
|
||||
}
|
||||
|
||||
private void DismissedHandler(Base control)
|
||||
{
|
||||
if (Dismissed != null)
|
||||
Dismissed.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
base.Layout(skin);
|
||||
|
||||
Align.PlaceDownLeft(m_Button, m_Label, 10);
|
||||
Align.CenterHorizontally(m_Button);
|
||||
m_InnerPanel.SizeToChildren();
|
||||
m_InnerPanel.Height += 10;
|
||||
SizeToChildren();
|
||||
}
|
||||
}
|
||||
}
|
152
Gwen/Control/NumericUpDown.cs
Normal file
152
Gwen/Control/NumericUpDown.cs
Normal file
|
@ -0,0 +1,152 @@
|
|||
using System;
|
||||
using Gwen.Control.Layout;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Numeric up/down.
|
||||
/// </summary>
|
||||
public class NumericUpDown : TextBoxNumeric
|
||||
{
|
||||
private int m_Max;
|
||||
private int m_Min;
|
||||
|
||||
private readonly Splitter m_Splitter;
|
||||
private readonly UpDownButton_Up m_Up;
|
||||
private readonly UpDownButton_Down m_Down;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum value.
|
||||
/// </summary>
|
||||
public int Min { get { return m_Min; } set { m_Min = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum value.
|
||||
/// </summary>
|
||||
public int Max { get { return m_Max; } set { m_Max = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NumericUpDown"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public NumericUpDown(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetSize(100, 20);
|
||||
|
||||
m_Splitter = new Splitter(this);
|
||||
m_Splitter.Dock = Pos.Right;
|
||||
m_Splitter.SetSize(13, 13);
|
||||
|
||||
m_Up = new UpDownButton_Up(m_Splitter);
|
||||
m_Up.Clicked += OnButtonUp;
|
||||
m_Up.IsTabable = false;
|
||||
m_Splitter.SetPanel(0, m_Up, false);
|
||||
|
||||
m_Down = new UpDownButton_Down(m_Splitter);
|
||||
m_Down.Clicked += OnButtonDown;
|
||||
m_Down.IsTabable = false;
|
||||
m_Down.Padding = new Padding(0, 1, 1, 0);
|
||||
m_Splitter.SetPanel(1, m_Down, false);
|
||||
|
||||
m_Max = 100;
|
||||
m_Min = 0;
|
||||
m_Value = 0f;
|
||||
Text = "0";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the value has been changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Up Arrow keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyUp(bool down)
|
||||
{
|
||||
if (down) OnButtonUp(null);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Down Arrow keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyDown(bool down)
|
||||
{
|
||||
if (down) OnButtonDown(null);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the button up event.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source.</param>
|
||||
protected virtual void OnButtonUp(Base control)
|
||||
{
|
||||
Value = m_Value + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the button down event.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source.</param>
|
||||
protected virtual void OnButtonDown(Base control)
|
||||
{
|
||||
Value = m_Value - 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the text can be assighed to the control.
|
||||
/// </summary>
|
||||
/// <param name="str">Text to evaluate.</param>
|
||||
/// <returns>True if the text is allowed.</returns>
|
||||
protected override bool IsTextAllowed(string str)
|
||||
{
|
||||
float d;
|
||||
if (!float.TryParse(str, out d))
|
||||
return false;
|
||||
if (d < m_Min) return false;
|
||||
if (d > m_Max) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Numeric value of the control.
|
||||
/// </summary>
|
||||
public override float Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.Value;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < m_Min) value = m_Min;
|
||||
if (value > m_Max) value = m_Max;
|
||||
if (value == m_Value) return;
|
||||
|
||||
base.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the text changed event.
|
||||
/// </summary>
|
||||
protected override void OnTextChanged()
|
||||
{
|
||||
base.OnTextChanged();
|
||||
if (ValueChanged != null)
|
||||
ValueChanged.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
72
Gwen/Control/ProgressBar.cs
Normal file
72
Gwen/Control/ProgressBar.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Progress bar.
|
||||
/// </summary>
|
||||
public class ProgressBar : Label
|
||||
{
|
||||
private bool m_Horizontal;
|
||||
private bool m_AutoLabel;
|
||||
private float m_Progress;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the control is horizontal.
|
||||
/// </summary>
|
||||
public bool IsHorizontal { get { return m_Horizontal; } set { m_Horizontal = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Progress value (0-1).
|
||||
/// </summary>
|
||||
public float Value
|
||||
{
|
||||
get { return m_Progress; }
|
||||
set
|
||||
{
|
||||
if (value < 0)
|
||||
value = 0;
|
||||
if (value > 1)
|
||||
value = 1;
|
||||
|
||||
m_Progress = value;
|
||||
if (m_AutoLabel)
|
||||
{
|
||||
int displayVal = (int)(m_Progress * 100);
|
||||
Text = displayVal.ToString() + "%";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the label text is autogenerated from value.
|
||||
/// </summary>
|
||||
public bool AutoLabel { get { return m_AutoLabel; } set { m_AutoLabel = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProgressBar"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public ProgressBar(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
MouseInputEnabled = false; // [omeg] what? was true
|
||||
SetSize(128, 32);
|
||||
TextPadding = Padding.Three;
|
||||
IsHorizontal = true;
|
||||
|
||||
Alignment = Pos.Center;
|
||||
m_Progress = 0;
|
||||
m_AutoLabel = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawProgressBar(this, m_Horizontal, m_Progress);
|
||||
}
|
||||
}
|
||||
}
|
108
Gwen/Control/Properties.cs
Normal file
108
Gwen/Control/Properties.cs
Normal file
|
@ -0,0 +1,108 @@
|
|||
using System;
|
||||
//using System.Windows.Forms;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Properties table.
|
||||
/// </summary>
|
||||
public class Properties : Base
|
||||
{
|
||||
private readonly SplitterBar m_SplitterBar;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the width of the first column (property names).
|
||||
/// </summary>
|
||||
public int SplitWidth { get { return m_SplitterBar.X; } } // todo: rename?
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a property value has been changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Properties"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Properties(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_SplitterBar = new SplitterBar(this);
|
||||
m_SplitterBar.SetPosition(80, 0);
|
||||
//m_SplitterBar.Cursor = Cursors.SizeWE;
|
||||
m_SplitterBar.Dragged += OnSplitterMoved;
|
||||
m_SplitterBar.ShouldDrawBackground = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function invoked after layout.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void PostLayout(Skin.Base skin)
|
||||
{
|
||||
m_SplitterBar.Height = 0;
|
||||
|
||||
if (SizeToChildren(false, true))
|
||||
{
|
||||
InvalidateParent();
|
||||
}
|
||||
|
||||
m_SplitterBar.SetSize(3, Height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the splitter moved event.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source.</param>
|
||||
protected virtual void OnSplitterMoved(Base control)
|
||||
{
|
||||
InvalidateChildren();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new text property row.
|
||||
/// </summary>
|
||||
/// <param name="label">Property name.</param>
|
||||
/// <param name="value">Initial value.</param>
|
||||
/// <returns>Newly created row.</returns>
|
||||
public PropertyRow Add(String label, String value="")
|
||||
{
|
||||
return Add(label, new Property.Text(this), value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new property row.
|
||||
/// </summary>
|
||||
/// <param name="label">Property name.</param>
|
||||
/// <param name="prop">Property control.</param>
|
||||
/// <param name="value">Initial value.</param>
|
||||
/// <returns>Newly created row.</returns>
|
||||
public PropertyRow Add(String label, Property.Base prop, String value="")
|
||||
{
|
||||
PropertyRow row = new PropertyRow(this, prop);
|
||||
row.Dock = Pos.Top;
|
||||
row.Label = label;
|
||||
row.ValueChanged += OnRowValueChanged;
|
||||
|
||||
prop.SetValue(value, true);
|
||||
|
||||
m_SplitterBar.BringToFront();
|
||||
return row;
|
||||
}
|
||||
|
||||
private void OnRowValueChanged(Base control)
|
||||
{
|
||||
if (ValueChanged != null)
|
||||
ValueChanged.Invoke(control);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all rows.
|
||||
/// </summary>
|
||||
public void DeleteAll()
|
||||
{
|
||||
m_InnerPanel.DeleteAllChildren();
|
||||
}
|
||||
}
|
||||
}
|
55
Gwen/Control/Property/Base.cs
Normal file
55
Gwen/Control/Property/Base.cs
Normal file
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control.Property
|
||||
{
|
||||
/// <summary>
|
||||
/// Base control for property entry.
|
||||
/// </summary>
|
||||
public class Base : Control.Base
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Base"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Base(Control.Base parent) : base(parent)
|
||||
{
|
||||
Height = 17;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the property value has been changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Property value (todo: always string, which is ugly. do something about it).
|
||||
/// </summary>
|
||||
public virtual String Value { get { return null; } set { SetValue(value, false); } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the property value is being edited.
|
||||
/// </summary>
|
||||
public virtual bool IsEditing { get { return false; } }
|
||||
|
||||
protected virtual void DoChanged()
|
||||
{
|
||||
if (ValueChanged != null)
|
||||
ValueChanged.Invoke(this);
|
||||
}
|
||||
|
||||
protected virtual void OnValueChanged(Control.Base control)
|
||||
{
|
||||
DoChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the property value.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to set.</param>
|
||||
/// <param name="fireEvents">Determines whether to fire "value changed" event.</param>
|
||||
public virtual void SetValue(String value, bool fireEvents = false)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
71
Gwen/Control/Property/Check.cs
Normal file
71
Gwen/Control/Property/Check.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control.Property
|
||||
{
|
||||
/// <summary>
|
||||
/// Checkable property.
|
||||
/// </summary>
|
||||
public class Check : Base
|
||||
{
|
||||
protected readonly Control.CheckBox m_CheckBox;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Check"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Check(Control.Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_CheckBox = new Control.CheckBox(this);
|
||||
m_CheckBox.ShouldDrawBackground = false;
|
||||
m_CheckBox.CheckChanged += OnValueChanged;
|
||||
m_CheckBox.IsTabable = true;
|
||||
m_CheckBox.KeyboardInputEnabled = true;
|
||||
m_CheckBox.SetPosition(2, 1);
|
||||
|
||||
Height = 18;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property value.
|
||||
/// </summary>
|
||||
public override string Value
|
||||
{
|
||||
get { return m_CheckBox.IsChecked ? "1" : "0"; }
|
||||
set { base.Value = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the property value.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to set.</param>
|
||||
/// <param name="fireEvents">Determines whether to fire "value changed" event.</param>
|
||||
public override void SetValue(string value, bool fireEvents = false)
|
||||
{
|
||||
if (value == "1" || value.ToLower() == "true" || value.ToLower() == "yes")
|
||||
{
|
||||
m_CheckBox.IsChecked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_CheckBox.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the property value is being edited.
|
||||
/// </summary>
|
||||
public override bool IsEditing
|
||||
{
|
||||
get { return m_CheckBox.HasFocus; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the control is hovered by mouse pointer.
|
||||
/// </summary>
|
||||
public override bool IsHovered
|
||||
{
|
||||
get { return base.IsHovered || m_CheckBox.IsHovered; }
|
||||
}
|
||||
}
|
||||
}
|
126
Gwen/Control/Property/Color.cs
Normal file
126
Gwen/Control/Property/Color.cs
Normal file
|
@ -0,0 +1,126 @@
|
|||
using System;
|
||||
using Gwen.ControlInternal;
|
||||
using Gwen.Input;
|
||||
|
||||
namespace Gwen.Control.Property
|
||||
{
|
||||
/// <summary>
|
||||
/// Color property.
|
||||
/// </summary>
|
||||
public class Color : Text
|
||||
{
|
||||
protected readonly ColorButton m_Button;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Color(Control.Base parent) : base(parent)
|
||||
{
|
||||
m_Button = new ColorButton(m_TextBox);
|
||||
m_Button.Dock = Pos.Right;
|
||||
m_Button.Width = 20;
|
||||
m_Button.Margin = new Margin(1, 1, 1, 2);
|
||||
m_Button.Clicked += OnButtonPressed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Color-select button press handler.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source.</param>
|
||||
protected virtual void OnButtonPressed(Control.Base control)
|
||||
{
|
||||
Menu menu = new Menu(GetCanvas());
|
||||
menu.SetSize(256, 180);
|
||||
menu.DeleteOnClose = true;
|
||||
menu.IconMarginDisabled = true;
|
||||
|
||||
HSVColorPicker picker = new HSVColorPicker(menu);
|
||||
picker.Dock = Pos.Fill;
|
||||
picker.SetSize(256, 128);
|
||||
|
||||
String[] split = m_TextBox.Text.Split(' ');
|
||||
|
||||
picker.SetColor(GetColorFromText(), false, true);
|
||||
picker.ColorChanged += OnColorChanged;
|
||||
|
||||
menu.Open(Pos.Right | Pos.Top);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Color changed handler.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source.</param>
|
||||
protected virtual void OnColorChanged(Control.Base control)
|
||||
{
|
||||
HSVColorPicker picker = control as HSVColorPicker;
|
||||
SetTextFromColor(picker.SelectedColor);
|
||||
DoChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property value.
|
||||
/// </summary>
|
||||
public override string Value
|
||||
{
|
||||
get { return m_TextBox.Text; }
|
||||
set { base.Value = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the property value.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to set.</param>
|
||||
/// <param name="fireEvents">Determines whether to fire "value changed" event.</param>
|
||||
public override void SetValue(string value, bool fireEvents = false)
|
||||
{
|
||||
m_TextBox.SetText(value, fireEvents);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the property value is being edited.
|
||||
/// </summary>
|
||||
public override bool IsEditing
|
||||
{
|
||||
get { return m_TextBox == InputHandler.KeyboardFocus; }
|
||||
}
|
||||
|
||||
private void SetTextFromColor(System.Drawing.Color color)
|
||||
{
|
||||
m_TextBox.Text = String.Format("{0} {1} {2}", color.R, color.G, color.B);
|
||||
}
|
||||
|
||||
private System.Drawing.Color GetColorFromText()
|
||||
{
|
||||
String[] split = m_TextBox.Text.Split(' ');
|
||||
|
||||
byte red = 0;
|
||||
byte green = 0;
|
||||
byte blue = 0;
|
||||
byte alpha = 255;
|
||||
|
||||
if (split.Length > 0 && split[0].Length > 0)
|
||||
{
|
||||
Byte.TryParse(split[0], out red);
|
||||
}
|
||||
|
||||
if (split.Length > 1 && split[1].Length > 0)
|
||||
{
|
||||
Byte.TryParse(split[1], out green);
|
||||
}
|
||||
|
||||
if (split.Length > 2 && split[2].Length > 0)
|
||||
{
|
||||
Byte.TryParse(split[2], out blue);
|
||||
}
|
||||
|
||||
return System.Drawing.Color.FromArgb(alpha, red, green, blue);
|
||||
}
|
||||
|
||||
protected override void DoChanged()
|
||||
{
|
||||
base.DoChanged();
|
||||
m_Button.Color = GetColorFromText();
|
||||
}
|
||||
}
|
||||
}
|
59
Gwen/Control/Property/Text.cs
Normal file
59
Gwen/Control/Property/Text.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control.Property
|
||||
{
|
||||
/// <summary>
|
||||
/// Text property.
|
||||
/// </summary>
|
||||
public class Text : Base
|
||||
{
|
||||
protected readonly TextBox m_TextBox;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Text"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Text(Control.Base parent) : base(parent)
|
||||
{
|
||||
m_TextBox = new TextBox(this);
|
||||
m_TextBox.Dock = Pos.Fill;
|
||||
m_TextBox.ShouldDrawBackground = false;
|
||||
m_TextBox.TextChanged += OnValueChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property value.
|
||||
/// </summary>
|
||||
public override string Value
|
||||
{
|
||||
get { return m_TextBox.Text; }
|
||||
set { base.Value = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the property value.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to set.</param>
|
||||
/// <param name="fireEvents">Determines whether to fire "value changed" event.</param>
|
||||
public override void SetValue(string value, bool fireEvents = false)
|
||||
{
|
||||
m_TextBox.SetText(value, fireEvents);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the property value is being edited.
|
||||
/// </summary>
|
||||
public override bool IsEditing
|
||||
{
|
||||
get { return m_TextBox.HasFocus; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the control is hovered by mouse pointer.
|
||||
/// </summary>
|
||||
public override bool IsHovered
|
||||
{
|
||||
get { return base.IsHovered | m_TextBox.IsHovered; }
|
||||
}
|
||||
}
|
||||
}
|
123
Gwen/Control/PropertyRow.cs
Normal file
123
Gwen/Control/PropertyRow.cs
Normal file
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Single property row.
|
||||
/// </summary>
|
||||
public class PropertyRow : Base
|
||||
{
|
||||
private readonly Label m_Label;
|
||||
private readonly Property.Base m_Property;
|
||||
private bool m_LastEditing;
|
||||
private bool m_LastHover;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the property value has changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the property value is being edited.
|
||||
/// </summary>
|
||||
public bool IsEditing { get { return m_Property != null && m_Property.IsEditing; } }
|
||||
|
||||
/// <summary>
|
||||
/// Property value.
|
||||
/// </summary>
|
||||
public String Value { get { return m_Property.Value; } set { m_Property.Value = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the control is hovered by mouse pointer.
|
||||
/// </summary>
|
||||
public override bool IsHovered
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.IsHovered || (m_Property != null && m_Property.IsHovered);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property name.
|
||||
/// </summary>
|
||||
public String Label { get { return m_Label.Text; } set { m_Label.Text = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertyRow"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
/// <param name="prop">Property control associated with this row.</param>
|
||||
public PropertyRow(Base parent, Property.Base prop)
|
||||
: base(parent)
|
||||
{
|
||||
PropertyRowLabel label = new PropertyRowLabel(this);
|
||||
label.Dock = Pos.Left;
|
||||
label.Alignment = Pos.Left | Pos.Top;
|
||||
label.Margin = new Margin(2, 2, 0, 0);
|
||||
m_Label = label;
|
||||
|
||||
m_Property = prop;
|
||||
m_Property.Parent = this;
|
||||
m_Property.Dock = Pos.Fill;
|
||||
m_Property.ValueChanged += OnValueChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
/* SORRY */
|
||||
if (IsEditing != m_LastEditing)
|
||||
{
|
||||
OnEditingChanged();
|
||||
m_LastEditing = IsEditing;
|
||||
}
|
||||
|
||||
if (IsHovered != m_LastHover)
|
||||
{
|
||||
OnHoverChanged();
|
||||
m_LastHover = IsHovered;
|
||||
}
|
||||
/* SORRY */
|
||||
|
||||
skin.DrawPropertyRow(this, m_Label.Right, IsEditing, IsHovered | m_Property.IsHovered);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
Properties parent = Parent as Properties;
|
||||
if (null == parent) return;
|
||||
|
||||
m_Label.Width = parent.SplitWidth;
|
||||
|
||||
if (m_Property != null)
|
||||
{
|
||||
Height = m_Property.Height;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnValueChanged(Base control)
|
||||
{
|
||||
if (ValueChanged != null)
|
||||
ValueChanged.Invoke(this);
|
||||
}
|
||||
|
||||
private void OnEditingChanged()
|
||||
{
|
||||
m_Label.Redraw();
|
||||
}
|
||||
|
||||
private void OnHoverChanged()
|
||||
{
|
||||
m_Label.Redraw();
|
||||
}
|
||||
}
|
||||
}
|
38
Gwen/Control/PropertyTree.cs
Normal file
38
Gwen/Control/PropertyTree.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Property table/tree.
|
||||
/// </summary>
|
||||
public class PropertyTree : TreeControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertyTree"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public PropertyTree(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new properties node.
|
||||
/// </summary>
|
||||
/// <param name="label">Node label.</param>
|
||||
/// <returns>Newly created control</returns>
|
||||
public Properties Add(String label)
|
||||
{
|
||||
TreeNode node = new PropertyTreeNode(this);
|
||||
node.Text = label;
|
||||
node.Dock = Pos.Top;
|
||||
|
||||
Properties props = new Properties(node);
|
||||
props.Dock = Pos.Top;
|
||||
|
||||
return props;
|
||||
}
|
||||
}
|
||||
}
|
39
Gwen/Control/RadioButton.cs
Normal file
39
Gwen/Control/RadioButton.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Radio button.
|
||||
/// </summary>
|
||||
public class RadioButton : CheckBox
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether unchecking is allowed.
|
||||
/// </summary>
|
||||
protected override bool AllowUncheck
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RadioButton"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public RadioButton(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetSize(15, 15);
|
||||
MouseInputEnabled = true;
|
||||
IsTabable = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawRadioButton(this, IsChecked, IsDepressed);
|
||||
}
|
||||
}
|
||||
}
|
137
Gwen/Control/RadioButtonGroup.cs
Normal file
137
Gwen/Control/RadioButtonGroup.cs
Normal file
|
@ -0,0 +1,137 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Radio button group.
|
||||
/// </summary>
|
||||
public class RadioButtonGroup : GroupBox
|
||||
{
|
||||
private LabeledRadioButton m_Selected;
|
||||
|
||||
/// <summary>
|
||||
/// Selected radio button.
|
||||
/// </summary>
|
||||
public LabeledRadioButton Selected { get { return m_Selected; } }
|
||||
|
||||
/// <summary>
|
||||
/// Internal name of the selected radio button.
|
||||
/// </summary>
|
||||
public String SelectedName { get { return m_Selected.Name; } }
|
||||
|
||||
/// <summary>
|
||||
/// Text of the selected radio button.
|
||||
/// </summary>
|
||||
public String SelectedLabel { get { return m_Selected.Text; } }
|
||||
|
||||
/// <summary>
|
||||
/// Index of the selected radio button.
|
||||
/// </summary>
|
||||
public int SelectedIndex { get { return Children.IndexOf(m_Selected); } }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the selected option has changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler SelectionChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RadioButtonGroup"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
/// <param name="label">Label for the outlining GroupBox.</param>
|
||||
public RadioButtonGroup(Base parent, String label)
|
||||
: base(parent)
|
||||
{
|
||||
IsTabable = false;
|
||||
KeyboardInputEnabled = true;
|
||||
Text = label;
|
||||
AutoSizeToContents = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new option.
|
||||
/// </summary>
|
||||
/// <param name="text">Option text.</param>
|
||||
/// <returns>Newly created control.</returns>
|
||||
public virtual LabeledRadioButton AddOption(String text)
|
||||
{
|
||||
return AddOption(text, String.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new option.
|
||||
/// </summary>
|
||||
/// <param name="text">Option text.</param>
|
||||
/// <param name="optionName">Internal name.</param>
|
||||
/// <returns>Newly created control.</returns>
|
||||
public virtual LabeledRadioButton AddOption(String text, String optionName)
|
||||
{
|
||||
LabeledRadioButton lrb = new LabeledRadioButton(this);
|
||||
lrb.Name = optionName;
|
||||
lrb.Text = text;
|
||||
lrb.RadioButton.Checked += OnRadioClicked;
|
||||
lrb.Dock = Pos.Top;
|
||||
lrb.Margin = new Margin(0, 0, 0, 1); // 1 bottom
|
||||
lrb.KeyboardInputEnabled = false; // todo: true?
|
||||
lrb.IsTabable = true;
|
||||
|
||||
Invalidate();
|
||||
return lrb;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the option change.
|
||||
/// </summary>
|
||||
/// <param name="fromPanel">Event source.</param>
|
||||
protected virtual void OnRadioClicked(Base fromPanel)
|
||||
{
|
||||
RadioButton @checked = fromPanel as RadioButton;
|
||||
foreach (LabeledRadioButton rb in Children.OfType<LabeledRadioButton>()) // todo: optimize
|
||||
{
|
||||
if (rb.RadioButton == @checked)
|
||||
m_Selected = rb;
|
||||
else
|
||||
rb.RadioButton.IsChecked = false;
|
||||
}
|
||||
|
||||
OnChanged();
|
||||
}
|
||||
/*
|
||||
/// <summary>
|
||||
/// Sizes to contents.
|
||||
/// </summary>
|
||||
public override void SizeToContents()
|
||||
{
|
||||
RecurseLayout(Skin); // options are docked so positions are not updated until layout runs
|
||||
//base.SizeToContents();
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
foreach (Base child in Children)
|
||||
{
|
||||
width = Math.Max(child.Width, width);
|
||||
height += child.Height;
|
||||
}
|
||||
SetSize(width, height);
|
||||
InvalidateParent();
|
||||
}
|
||||
*/
|
||||
protected virtual void OnChanged()
|
||||
{
|
||||
if (SelectionChanged != null)
|
||||
SelectionChanged.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects the specified option.
|
||||
/// </summary>
|
||||
/// <param name="index">Option to select.</param>
|
||||
public void SetSelection(int index)
|
||||
{
|
||||
if (index < 0 || index >= Children.Count)
|
||||
return;
|
||||
|
||||
(Children[index] as LabeledRadioButton).RadioButton.Press();
|
||||
}
|
||||
}
|
||||
}
|
160
Gwen/Control/ResizableControl.cs
Normal file
160
Gwen/Control/ResizableControl.cs
Normal file
|
@ -0,0 +1,160 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Base resizable control.
|
||||
/// </summary>
|
||||
public class ResizableControl : Base
|
||||
{
|
||||
private bool m_ClampMovement;
|
||||
private readonly Resizer[] m_Resizer;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether control's position should be restricted to its parent bounds.
|
||||
/// </summary>
|
||||
public bool ClampMovement { get { return m_ClampMovement; } set { m_ClampMovement = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the control has been resized.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Resized;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ResizableControl"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public ResizableControl(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_Resizer = new Resizer[10];
|
||||
MinimumSize = new Point(5, 5);
|
||||
m_ClampMovement = false;
|
||||
|
||||
m_Resizer[2] = new Resizer(this);
|
||||
m_Resizer[2].Dock = Pos.Bottom;
|
||||
m_Resizer[2].ResizeDir = Pos.Bottom;
|
||||
m_Resizer[2].Resized += OnResized;
|
||||
m_Resizer[2].Target = this;
|
||||
|
||||
m_Resizer[1] = new Resizer(m_Resizer[2]);
|
||||
m_Resizer[1].Dock = Pos.Left;
|
||||
m_Resizer[1].ResizeDir = Pos.Bottom | Pos.Left;
|
||||
m_Resizer[1].Resized += OnResized;
|
||||
m_Resizer[1].Target = this;
|
||||
|
||||
m_Resizer[3] = new Resizer(m_Resizer[2]);
|
||||
m_Resizer[3].Dock = Pos.Right;
|
||||
m_Resizer[3].ResizeDir = Pos.Bottom | Pos.Right;
|
||||
m_Resizer[3].Resized += OnResized;
|
||||
m_Resizer[3].Target = this;
|
||||
|
||||
m_Resizer[8] = new Resizer(this);
|
||||
m_Resizer[8].Dock = Pos.Top;
|
||||
m_Resizer[8].ResizeDir = Pos.Top;
|
||||
m_Resizer[8].Resized += OnResized;
|
||||
m_Resizer[8].Target = this;
|
||||
|
||||
m_Resizer[7] = new Resizer(m_Resizer[8]);
|
||||
m_Resizer[7].Dock = Pos.Left;
|
||||
m_Resizer[7].ResizeDir = Pos.Left | Pos.Top;
|
||||
m_Resizer[7].Resized += OnResized;
|
||||
m_Resizer[7].Target = this;
|
||||
|
||||
m_Resizer[9] = new Resizer(m_Resizer[8]);
|
||||
m_Resizer[9].Dock = Pos.Right;
|
||||
m_Resizer[9].ResizeDir = Pos.Right | Pos.Top;
|
||||
m_Resizer[9].Resized += OnResized;
|
||||
m_Resizer[9].Target = this;
|
||||
|
||||
m_Resizer[4] = new Resizer(this);
|
||||
m_Resizer[4].Dock = Pos.Left;
|
||||
m_Resizer[4].ResizeDir = Pos.Left;
|
||||
m_Resizer[4].Resized += OnResized;
|
||||
m_Resizer[4].Target = this;
|
||||
|
||||
m_Resizer[6] = new Resizer(this);
|
||||
m_Resizer[6].Dock = Pos.Right;
|
||||
m_Resizer[6].ResizeDir = Pos.Right;
|
||||
m_Resizer[6].Resized += OnResized;
|
||||
m_Resizer[6].Target = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the resized event.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source.</param>
|
||||
protected virtual void OnResized(Base control)
|
||||
{
|
||||
if (Resized != null)
|
||||
Resized.Invoke(this);
|
||||
}
|
||||
|
||||
protected Resizer GetResizer(int i)
|
||||
{
|
||||
return m_Resizer[i];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables resizing.
|
||||
/// </summary>
|
||||
public void DisableResizing()
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
if (m_Resizer[i] == null)
|
||||
continue;
|
||||
m_Resizer[i].MouseInputEnabled = false;
|
||||
m_Resizer[i].IsHidden = true;
|
||||
Padding = new Padding(m_Resizer[i].Width, m_Resizer[i].Width, m_Resizer[i].Width, m_Resizer[i].Width);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables resizing.
|
||||
/// </summary>
|
||||
public void EnableResizing()
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
if (m_Resizer[i] == null)
|
||||
continue;
|
||||
m_Resizer[i].MouseInputEnabled = true;
|
||||
m_Resizer[i].IsHidden = false;
|
||||
Padding = new Padding(0, 0, 0, 0); // todo: check if ok
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the control bounds.
|
||||
/// </summary>
|
||||
/// <param name="x">X position.</param>
|
||||
/// <param name="y">Y position.</param>
|
||||
/// <param name="width">Width.</param>
|
||||
/// <param name="height">Height.</param>
|
||||
/// <returns>
|
||||
/// True if bounds changed.
|
||||
/// </returns>
|
||||
public override bool SetBounds(int x, int y, int width, int height)
|
||||
{
|
||||
Point minSize = MinimumSize;
|
||||
// Clamp Minimum Size
|
||||
if (width < minSize.X) width = minSize.X;
|
||||
if (height < minSize.Y) height = minSize.Y;
|
||||
|
||||
// Clamp to parent's window
|
||||
Base parent = Parent;
|
||||
if (parent != null && m_ClampMovement)
|
||||
{
|
||||
if (x + width > parent.Width) x = parent.Width - width;
|
||||
if (x < 0) x = 0;
|
||||
if (y + height > parent.Height) y = parent.Height - height;
|
||||
if (y < 0) y = 0;
|
||||
}
|
||||
|
||||
return base.SetBounds(x, y, width, height);
|
||||
}
|
||||
}
|
||||
}
|
254
Gwen/Control/RichLabel.cs
Normal file
254
Gwen/Control/RichLabel.cs
Normal file
|
@ -0,0 +1,254 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Multiline label with text chunks having different color/font.
|
||||
/// </summary>
|
||||
public class RichLabel : Base
|
||||
{
|
||||
protected struct TextBlock
|
||||
{
|
||||
public BlockType Type;
|
||||
public String Text;
|
||||
public Color Color;
|
||||
public Font Font;
|
||||
}
|
||||
|
||||
protected enum BlockType
|
||||
{
|
||||
Text,
|
||||
NewLine
|
||||
}
|
||||
|
||||
private bool m_NeedsRebuild;
|
||||
private readonly List<TextBlock> m_TextBlocks;
|
||||
private readonly String[] newline;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RichLabel"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public RichLabel(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
newline = new string[] { Environment.NewLine };
|
||||
m_TextBlocks = new List<TextBlock>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a line break to the control.
|
||||
/// </summary>
|
||||
public void AddLineBreak()
|
||||
{
|
||||
TextBlock block = new TextBlock { Type = BlockType.NewLine };
|
||||
m_TextBlocks.Add(block);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds text to the control.
|
||||
/// </summary>
|
||||
/// <param name="text">Text to add.</param>
|
||||
/// <param name="color">Text color.</param>
|
||||
/// <param name="font">Font to use.</param>
|
||||
public void AddText(String text, Color color, Font font = null)
|
||||
{
|
||||
if (String.IsNullOrEmpty(text))
|
||||
return;
|
||||
|
||||
var lines = text.Split(newline, StringSplitOptions.None);
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
AddLineBreak();
|
||||
|
||||
TextBlock block = new TextBlock { Type = BlockType.Text, Text = lines[i], Color = color, Font = font };
|
||||
|
||||
m_TextBlocks.Add(block);
|
||||
m_NeedsRebuild = true;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes the control to fit its children.
|
||||
/// </summary>
|
||||
/// <param name="width">Determines whether to change control's width.</param>
|
||||
/// <param name="height">Determines whether to change control's height.</param>
|
||||
/// <returns>
|
||||
/// True if bounds changed.
|
||||
/// </returns>
|
||||
public override bool SizeToChildren(bool width = true, bool height = true)
|
||||
{
|
||||
Rebuild();
|
||||
return base.SizeToChildren(width, height);
|
||||
}
|
||||
|
||||
protected void SplitLabel(String text, Font font, TextBlock block, ref int x, ref int y, ref int lineHeight)
|
||||
{
|
||||
var spaced = Util.SplitAndKeep(text, " ");
|
||||
if (spaced.Length == 0)
|
||||
return;
|
||||
|
||||
int spaceLeft = Width - x;
|
||||
String leftOver;
|
||||
|
||||
// Does the whole word fit in?
|
||||
Point stringSize = Skin.Renderer.MeasureText(font, text);
|
||||
if (spaceLeft > stringSize.X)
|
||||
{
|
||||
CreateLabel(text, block, ref x, ref y, ref lineHeight, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the first word is bigger than the line, just give up.
|
||||
Point wordSize = Skin.Renderer.MeasureText(font, spaced[0]);
|
||||
if (wordSize.X >= spaceLeft)
|
||||
{
|
||||
CreateLabel(spaced[0], block, ref x, ref y, ref lineHeight, true);
|
||||
if (spaced[0].Length >= text.Length)
|
||||
return;
|
||||
|
||||
leftOver = text.Substring(spaced[0].Length + 1);
|
||||
SplitLabel(leftOver, font, block, ref x, ref y, ref lineHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
String newString = String.Empty;
|
||||
for (int i = 0; i < spaced.Length; i++)
|
||||
{
|
||||
wordSize = Skin.Renderer.MeasureText(font, newString + spaced[i]);
|
||||
if (wordSize.X > spaceLeft)
|
||||
{
|
||||
CreateLabel(newString, block, ref x, ref y, ref lineHeight, true);
|
||||
x = 0;
|
||||
y += lineHeight;
|
||||
break;
|
||||
}
|
||||
|
||||
newString += spaced[i];
|
||||
}
|
||||
|
||||
int newstr_len = newString.Length;
|
||||
if (newstr_len < text.Length)
|
||||
{
|
||||
leftOver = text.Substring(newstr_len + 1);
|
||||
SplitLabel(leftOver, font, block, ref x, ref y, ref lineHeight);
|
||||
}
|
||||
}
|
||||
|
||||
protected void CreateLabel(String text, TextBlock block, ref int x, ref int y, ref int lineHeight, bool noSplit)
|
||||
{
|
||||
// Use default font or is one set?
|
||||
Font font = Skin.DefaultFont;
|
||||
if (block.Font != null)
|
||||
font = block.Font;
|
||||
|
||||
// This string is too long for us, split it up.
|
||||
Point p = Skin.Renderer.MeasureText(font, text);
|
||||
|
||||
if (lineHeight == -1)
|
||||
{
|
||||
lineHeight = p.Y;
|
||||
}
|
||||
|
||||
if (!noSplit)
|
||||
{
|
||||
if (x + p.X > Width)
|
||||
{
|
||||
SplitLabel(text, font, block, ref x, ref y, ref lineHeight);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap
|
||||
if (x + p.X >= Width)
|
||||
{
|
||||
CreateNewline(ref x, ref y, lineHeight);
|
||||
}
|
||||
|
||||
Label label = new Label(this);
|
||||
label.SetText(x == 0 ? text.TrimStart(' ') : text);
|
||||
label.TextColor = block.Color;
|
||||
label.Font = font;
|
||||
label.SizeToContents();
|
||||
label.SetPosition(x, y);
|
||||
|
||||
//lineheight = (lineheight + pLabel.Height()) / 2;
|
||||
|
||||
x += label.Width;
|
||||
|
||||
if (x >= Width)
|
||||
{
|
||||
CreateNewline(ref x, ref y, lineHeight);
|
||||
}
|
||||
}
|
||||
|
||||
protected void CreateNewline(ref int x, ref int y, int lineHeight)
|
||||
{
|
||||
x = 0;
|
||||
y += lineHeight;
|
||||
}
|
||||
|
||||
protected void Rebuild()
|
||||
{
|
||||
DeleteAllChildren();
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int lineHeight = -1;
|
||||
|
||||
|
||||
foreach (var block in m_TextBlocks)
|
||||
{
|
||||
if (block.Type == BlockType.NewLine)
|
||||
{
|
||||
CreateNewline(ref x, ref y, lineHeight);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (block.Type == BlockType.Text)
|
||||
{
|
||||
CreateLabel(block.Text, block, ref x, ref y, ref lineHeight, false);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
m_NeedsRebuild = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked when control's bounds change.
|
||||
/// </summary>
|
||||
/// <param name="oldBounds">Old bounds.</param>
|
||||
protected override void OnBoundsChanged(Rectangle oldBounds)
|
||||
{
|
||||
base.OnBoundsChanged(oldBounds);
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
base.Layout(skin);
|
||||
if (m_NeedsRebuild)
|
||||
Rebuild();
|
||||
|
||||
// align bottoms. this is still not ideal, need to take font metrics into account.
|
||||
Base prev = null;
|
||||
foreach (Base child in Children)
|
||||
{
|
||||
if (prev != null && child.Y == prev.Y)
|
||||
{
|
||||
Align.PlaceRightBottom(child, prev);
|
||||
}
|
||||
prev = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
133
Gwen/Control/ScrollBar.cs
Normal file
133
Gwen/Control/ScrollBar.cs
Normal file
|
@ -0,0 +1,133 @@
|
|||
using System;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for scrollbars.
|
||||
/// </summary>
|
||||
public class ScrollBar : Base
|
||||
{
|
||||
protected readonly ScrollBarButton[] m_ScrollButton;
|
||||
protected readonly ScrollBarBar m_Bar;
|
||||
|
||||
protected bool m_Depressed;
|
||||
protected float m_ScrollAmount;
|
||||
protected float m_ContentSize;
|
||||
protected float m_ViewableContentSize;
|
||||
protected float m_NudgeAmount;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the bar is moved.
|
||||
/// </summary>
|
||||
public event GwenEventHandler BarMoved;
|
||||
|
||||
/// <summary>
|
||||
/// Bar size (in pixels).
|
||||
/// </summary>
|
||||
public virtual int BarSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Bar position (in pixels).
|
||||
/// </summary>
|
||||
public virtual int BarPos { get { return 0; } }
|
||||
|
||||
/// <summary>
|
||||
/// Button size (in pixels).
|
||||
/// </summary>
|
||||
public virtual int ButtonSize { get { return 0; } }
|
||||
|
||||
public virtual float NudgeAmount { get { return m_NudgeAmount / m_ContentSize; } set { m_NudgeAmount = value; } }
|
||||
public float ScrollAmount { get { return m_ScrollAmount; } }
|
||||
public float ContentSize { get { return m_ContentSize; } set { if (m_ContentSize != value) Invalidate(); m_ContentSize = value; } }
|
||||
public float ViewableContentSize { get { return m_ViewableContentSize; } set { if (m_ViewableContentSize != value) Invalidate(); m_ViewableContentSize = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the bar is horizontal.
|
||||
/// </summary>
|
||||
public virtual bool IsHorizontal { get { return false; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScrollBar"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
protected ScrollBar(Base parent) : base(parent)
|
||||
{
|
||||
m_ScrollButton = new ScrollBarButton[2];
|
||||
m_ScrollButton[0] = new ScrollBarButton(this);
|
||||
m_ScrollButton[1] = new ScrollBarButton(this);
|
||||
|
||||
m_Bar = new ScrollBarBar(this);
|
||||
|
||||
SetBounds(0, 0, 15, 15);
|
||||
m_Depressed = false;
|
||||
|
||||
m_ScrollAmount = 0;
|
||||
m_ContentSize = 0;
|
||||
m_ViewableContentSize = 0;
|
||||
|
||||
NudgeAmount = 20;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the scroll amount (0-1).
|
||||
/// </summary>
|
||||
/// <param name="value">Scroll amount.</param>
|
||||
/// <param name="forceUpdate">Determines whether the control should be updated.</param>
|
||||
/// <returns>True if control state changed.</returns>
|
||||
public virtual bool SetScrollAmount(float value, bool forceUpdate = false)
|
||||
{
|
||||
if (m_ScrollAmount == value && !forceUpdate)
|
||||
return false;
|
||||
m_ScrollAmount = value;
|
||||
Invalidate();
|
||||
OnBarMoved(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="down">If set to <c>true</c> mouse button is down.</param>
|
||||
protected override void OnMouseClickedLeft(int x, int y, bool down)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawScrollBar(this, IsHorizontal, m_Depressed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the BarMoved event.
|
||||
/// </summary>
|
||||
/// <param name="control">The control.</param>
|
||||
protected virtual void OnBarMoved(Base control)
|
||||
{
|
||||
if (BarMoved != null)
|
||||
BarMoved.Invoke(this);
|
||||
}
|
||||
|
||||
protected virtual float CalculateScrolledAmount()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected virtual int CalculateBarSize()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public virtual void ScrollToLeft() { }
|
||||
public virtual void ScrollToRight() { }
|
||||
public virtual void ScrollToTop() { }
|
||||
public virtual void ScrollToBottom() { }
|
||||
}
|
||||
}
|
301
Gwen/Control/ScrollControl.cs
Normal file
301
Gwen/Control/ScrollControl.cs
Normal file
|
@ -0,0 +1,301 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Base for controls whose interior can be scrolled.
|
||||
/// </summary>
|
||||
public class ScrollControl : Base
|
||||
{
|
||||
private bool m_CanScrollH;
|
||||
private bool m_CanScrollV;
|
||||
private bool m_AutoHideBars;
|
||||
|
||||
private readonly ScrollBar m_VerticalScrollBar;
|
||||
private readonly ScrollBar m_HorizontalScrollBar;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the control can be scrolled horizontally.
|
||||
/// </summary>
|
||||
public bool CanScrollH { get { return m_CanScrollH; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the control can be scrolled vertically.
|
||||
/// </summary>
|
||||
public bool CanScrollV { get { return m_CanScrollV; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the scroll bars should be hidden if not needed.
|
||||
/// </summary>
|
||||
public bool AutoHideBars { get { return m_AutoHideBars; } set { m_AutoHideBars = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScrollControl"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public ScrollControl(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
MouseInputEnabled = false;
|
||||
|
||||
m_VerticalScrollBar = new VerticalScrollBar(this);
|
||||
m_VerticalScrollBar.Dock = Pos.Right;
|
||||
m_VerticalScrollBar.BarMoved += VBarMoved;
|
||||
m_CanScrollV = true;
|
||||
m_VerticalScrollBar.NudgeAmount = 30;
|
||||
|
||||
m_HorizontalScrollBar = new HorizontalScrollBar(this);
|
||||
m_HorizontalScrollBar.Dock = Pos.Bottom;
|
||||
m_HorizontalScrollBar.BarMoved += HBarMoved;
|
||||
m_CanScrollH = true;
|
||||
m_HorizontalScrollBar.NudgeAmount = 30;
|
||||
|
||||
m_InnerPanel = new Base(this);
|
||||
m_InnerPanel.SetPosition(0, 0);
|
||||
m_InnerPanel.Margin = Margin.Five;
|
||||
m_InnerPanel.SendToBack();
|
||||
m_InnerPanel.MouseInputEnabled = false;
|
||||
|
||||
m_AutoHideBars = false;
|
||||
}
|
||||
|
||||
protected bool HScrollRequired
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
m_HorizontalScrollBar.SetScrollAmount(0, true);
|
||||
m_HorizontalScrollBar.IsDisabled = true;
|
||||
if (m_AutoHideBars)
|
||||
m_HorizontalScrollBar.IsHidden = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HorizontalScrollBar.IsHidden = false;
|
||||
m_HorizontalScrollBar.IsDisabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected bool VScrollRequired
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
m_VerticalScrollBar.SetScrollAmount(0, true);
|
||||
m_VerticalScrollBar.IsDisabled = true;
|
||||
if (m_AutoHideBars)
|
||||
m_VerticalScrollBar.IsHidden = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_VerticalScrollBar.IsHidden = false;
|
||||
m_VerticalScrollBar.IsDisabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables inner scrollbars.
|
||||
/// </summary>
|
||||
/// <param name="horizontal">Determines whether the horizontal scrollbar should be enabled.</param>
|
||||
/// <param name="vertical">Determines whether the vertical scrollbar should be enabled.</param>
|
||||
public virtual void EnableScroll(bool horizontal, bool vertical)
|
||||
{
|
||||
m_CanScrollV = vertical;
|
||||
m_CanScrollH = horizontal;
|
||||
m_VerticalScrollBar.IsHidden = !m_CanScrollV;
|
||||
m_HorizontalScrollBar.IsHidden = !m_CanScrollH;
|
||||
}
|
||||
|
||||
public virtual void SetInnerSize(int width, int height)
|
||||
{
|
||||
m_InnerPanel.SetSize(width, height);
|
||||
}
|
||||
|
||||
protected virtual void VBarMoved(Base control)
|
||||
{
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected virtual void HBarMoved(Base control)
|
||||
{
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked when control children's bounds change.
|
||||
/// </summary>
|
||||
/// <param name="oldChildBounds"></param>
|
||||
/// <param name="child"></param>
|
||||
protected override void OnChildBoundsChanged(System.Drawing.Rectangle oldChildBounds, Base child)
|
||||
{
|
||||
UpdateScrollBars();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
UpdateScrollBars();
|
||||
base.Layout(skin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse wheel event.
|
||||
/// </summary>
|
||||
/// <param name="delta">Scroll delta.</param>
|
||||
/// <returns></returns>
|
||||
protected override bool OnMouseWheeled(int delta)
|
||||
{
|
||||
if (CanScrollV && m_VerticalScrollBar.IsVisible)
|
||||
{
|
||||
if (m_VerticalScrollBar.SetScrollAmount(
|
||||
m_VerticalScrollBar.ScrollAmount - m_VerticalScrollBar.NudgeAmount * (delta / 60.0f), true))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CanScrollH && m_HorizontalScrollBar.IsVisible)
|
||||
{
|
||||
if (m_HorizontalScrollBar.SetScrollAmount(
|
||||
m_HorizontalScrollBar.ScrollAmount - m_HorizontalScrollBar.NudgeAmount * (delta / 60.0f), true))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
#if false
|
||||
|
||||
// Debug render - this shouldn't render ANYTHING REALLY - it should be up to the parent!
|
||||
|
||||
Gwen::Rect rect = GetRenderBounds();
|
||||
Gwen::Renderer::Base* render = skin->GetRender();
|
||||
|
||||
render->SetDrawColor( Gwen::Color( 255, 255, 0, 100 ) );
|
||||
render->DrawFilledRect( rect );
|
||||
|
||||
render->SetDrawColor( Gwen::Color( 255, 0, 0, 100 ) );
|
||||
render->DrawFilledRect( m_InnerPanel->GetBounds() );
|
||||
|
||||
render->RenderText( skin->GetDefaultFont(), Gwen::Point( 0, 0 ), Utility::Format( L"Offset: %i %i", m_InnerPanel->X(), m_InnerPanel->Y() ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
public virtual void UpdateScrollBars()
|
||||
{
|
||||
if (null == m_InnerPanel)
|
||||
return;
|
||||
|
||||
//Get the max size of all our children together
|
||||
int childrenWidth = Children.Count > 0 ? Children.Max(x => x.Right) : 0;
|
||||
int childrenHeight = Children.Count > 0 ? Children.Max(x => x.Bottom) : 0;
|
||||
|
||||
if (m_CanScrollH)
|
||||
{
|
||||
m_InnerPanel.SetSize(Math.Max(Width, childrenWidth), Math.Max(Height, childrenHeight));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_InnerPanel.SetSize(Width - (m_VerticalScrollBar.IsHidden ? 0 : m_VerticalScrollBar.Width),
|
||||
Math.Max(Height, childrenHeight));
|
||||
}
|
||||
|
||||
float wPercent = Width/
|
||||
(float) (childrenWidth + (m_VerticalScrollBar.IsHidden ? 0 : m_VerticalScrollBar.Width));
|
||||
float hPercent = Height/
|
||||
(float)
|
||||
(childrenHeight + (m_HorizontalScrollBar.IsHidden ? 0 : m_HorizontalScrollBar.Height));
|
||||
|
||||
if (m_CanScrollV)
|
||||
VScrollRequired = hPercent >= 1;
|
||||
else
|
||||
m_VerticalScrollBar.IsHidden = true;
|
||||
|
||||
if (m_CanScrollH)
|
||||
HScrollRequired = wPercent >= 1;
|
||||
else
|
||||
m_HorizontalScrollBar.IsHidden = true;
|
||||
|
||||
|
||||
m_VerticalScrollBar.ContentSize = m_InnerPanel.Height;
|
||||
m_VerticalScrollBar.ViewableContentSize = Height - (m_HorizontalScrollBar.IsHidden ? 0 : m_HorizontalScrollBar.Height);
|
||||
|
||||
|
||||
m_HorizontalScrollBar.ContentSize = m_InnerPanel.Width;
|
||||
m_HorizontalScrollBar.ViewableContentSize = Width - (m_VerticalScrollBar.IsHidden ? 0 : m_VerticalScrollBar.Width);
|
||||
|
||||
int newInnerPanelPosX = 0;
|
||||
int newInnerPanelPosY = 0;
|
||||
|
||||
if (CanScrollV && !m_VerticalScrollBar.IsHidden)
|
||||
{
|
||||
newInnerPanelPosY =
|
||||
(int)(
|
||||
-((m_InnerPanel.Height) - Height + (m_HorizontalScrollBar.IsHidden ? 0 : m_HorizontalScrollBar.Height))*
|
||||
m_VerticalScrollBar.ScrollAmount);
|
||||
}
|
||||
if (CanScrollH && !m_HorizontalScrollBar.IsHidden)
|
||||
{
|
||||
newInnerPanelPosX =
|
||||
(int)(
|
||||
-((m_InnerPanel.Width) - Width + (m_VerticalScrollBar.IsHidden ? 0 : m_VerticalScrollBar.Width))*
|
||||
m_HorizontalScrollBar.ScrollAmount);
|
||||
}
|
||||
|
||||
m_InnerPanel.SetPosition(newInnerPanelPosX, newInnerPanelPosY);
|
||||
}
|
||||
|
||||
public virtual void ScrollToBottom()
|
||||
{
|
||||
if (!CanScrollV)
|
||||
return;
|
||||
|
||||
UpdateScrollBars();
|
||||
m_VerticalScrollBar.ScrollToBottom();
|
||||
}
|
||||
|
||||
public virtual void ScrollToTop()
|
||||
{
|
||||
if (CanScrollV)
|
||||
{
|
||||
UpdateScrollBars();
|
||||
m_VerticalScrollBar.ScrollToTop();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ScrollToLeft()
|
||||
{
|
||||
if (CanScrollH)
|
||||
{
|
||||
UpdateScrollBars();
|
||||
m_VerticalScrollBar.ScrollToLeft();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ScrollToRight()
|
||||
{
|
||||
if (CanScrollH)
|
||||
{
|
||||
UpdateScrollBars();
|
||||
m_VerticalScrollBar.ScrollToRight();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void DeleteAll()
|
||||
{
|
||||
m_InnerPanel.DeleteAllChildren();
|
||||
}
|
||||
}
|
||||
}
|
236
Gwen/Control/Slider.cs
Normal file
236
Gwen/Control/Slider.cs
Normal file
|
@ -0,0 +1,236 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.ControlInternal;
|
||||
using Gwen.Input;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Base slider.
|
||||
/// </summary>
|
||||
public class Slider : Base
|
||||
{
|
||||
protected readonly SliderBar m_SliderBar;
|
||||
protected bool m_SnapToNotches;
|
||||
protected int m_NotchCount;
|
||||
protected float m_Value;
|
||||
protected float m_Min;
|
||||
protected float m_Max;
|
||||
|
||||
/// <summary>
|
||||
/// Number of notches on the slider axis.
|
||||
/// </summary>
|
||||
public int NotchCount { get { return m_NotchCount; } set { m_NotchCount = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the slider should snap to notches.
|
||||
/// </summary>
|
||||
public bool SnapToNotches { get { return m_SnapToNotches; } set { m_SnapToNotches = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum value.
|
||||
/// </summary>
|
||||
public float Min { get { return m_Min; } set { SetRange(value, m_Max); } }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum value.
|
||||
/// </summary>
|
||||
public float Max { get { return m_Max; } set { SetRange(m_Min, value); } }
|
||||
|
||||
/// <summary>
|
||||
/// Current value.
|
||||
/// </summary>
|
||||
public float Value
|
||||
{
|
||||
get { return m_Min + (m_Value * (m_Max - m_Min)); }
|
||||
set
|
||||
{
|
||||
if (value < m_Min) value = m_Min;
|
||||
if (value > m_Max) value = m_Max;
|
||||
// Normalize Value
|
||||
value = (value - m_Min) / (m_Max - m_Min);
|
||||
SetValueInternal(value);
|
||||
Redraw();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the value has been changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Slider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
protected Slider(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetBounds(new Rectangle(0, 0, 32, 128));
|
||||
|
||||
m_SliderBar = new SliderBar(this);
|
||||
m_SliderBar.Dragged += OnMoved;
|
||||
|
||||
m_Min = 0.0f;
|
||||
m_Max = 1.0f;
|
||||
|
||||
m_SnapToNotches = false;
|
||||
m_NotchCount = 5;
|
||||
m_Value = 0.0f;
|
||||
|
||||
KeyboardInputEnabled = true;
|
||||
IsTabable = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Right Arrow keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyRight(bool down)
|
||||
{
|
||||
if (down)
|
||||
Value = Value + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Up Arrow keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyUp(bool down)
|
||||
{
|
||||
if (down)
|
||||
Value = Value + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Left Arrow keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyLeft(bool down)
|
||||
{
|
||||
if (down)
|
||||
Value = Value - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Down Arrow keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyDown(bool down)
|
||||
{
|
||||
if (down)
|
||||
Value = Value - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Home keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyHome(bool down)
|
||||
{
|
||||
if (down)
|
||||
Value = m_Min;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for End keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyEnd(bool down)
|
||||
{
|
||||
if (down)
|
||||
Value = m_Max;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="down">If set to <c>true</c> mouse button is down.</param>
|
||||
protected override void OnMouseClickedLeft(int x, int y, bool down)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void OnMoved(Base control)
|
||||
{
|
||||
SetValueInternal(CalculateValue());
|
||||
}
|
||||
|
||||
protected virtual float CalculateValue()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected virtual void UpdateBarFromValue()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void SetValueInternal(float val)
|
||||
{
|
||||
if (m_SnapToNotches)
|
||||
{
|
||||
val = (float)Math.Floor((val * m_NotchCount) + 0.5f);
|
||||
val /= m_NotchCount;
|
||||
}
|
||||
|
||||
if (m_Value != val)
|
||||
{
|
||||
m_Value = val;
|
||||
if (ValueChanged != null)
|
||||
ValueChanged.Invoke(this);
|
||||
}
|
||||
|
||||
UpdateBarFromValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value range.
|
||||
/// </summary>
|
||||
/// <param name="min">Minimum value.</param>
|
||||
/// <param name="max">Maximum value.</param>
|
||||
public void SetRange(float min, float max)
|
||||
{
|
||||
m_Min = min;
|
||||
m_Max = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the focus overlay.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void RenderFocus(Skin.Base skin)
|
||||
{
|
||||
if (InputHandler.KeyboardFocus != this) return;
|
||||
if (!IsTabable) return;
|
||||
|
||||
skin.DrawKeyboardHighlight(this, RenderBounds, 0);
|
||||
}
|
||||
}
|
||||
}
|
44
Gwen/Control/StatusBar.cs
Normal file
44
Gwen/Control/StatusBar.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Status bar.
|
||||
/// </summary>
|
||||
public class StatusBar : Label
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StatusBar"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public StatusBar(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
Height = 22;
|
||||
Dock = Pos.Bottom;
|
||||
Padding = Padding.Two;
|
||||
//Text = "Status Bar"; // [omeg] todo i18n
|
||||
Alignment = Pos.Left | Pos.CenterV;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a control to the bar.
|
||||
/// </summary>
|
||||
/// <param name="control">Control to add.</param>
|
||||
/// <param name="right">Determines whether the control should be added to the right side of the bar.</param>
|
||||
public void AddControl(Base control, bool right)
|
||||
{
|
||||
control.Parent = this;
|
||||
control.Dock = right ? Pos.Right : Pos.Left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawStatusBar(this);
|
||||
}
|
||||
}
|
||||
}
|
203
Gwen/Control/TabButton.cs
Normal file
203
Gwen/Control/TabButton.cs
Normal file
|
@ -0,0 +1,203 @@
|
|||
using System;
|
||||
using Gwen.Input;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Tab header.
|
||||
/// </summary>
|
||||
public class TabButton : Button
|
||||
{
|
||||
private Base m_Page;
|
||||
private TabControl m_Control;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the tab is active.
|
||||
/// </summary>
|
||||
public bool IsActive { get { return m_Page != null && m_Page.IsVisible; } }
|
||||
|
||||
// todo: remove public access
|
||||
public TabControl TabControl
|
||||
{
|
||||
get { return m_Control; }
|
||||
set
|
||||
{
|
||||
if (value == m_Control) return;
|
||||
if (m_Control != null)
|
||||
m_Control.OnLoseTab(this);
|
||||
m_Control = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interior of the tab.
|
||||
/// </summary>
|
||||
public Base Page { get { return m_Page; } set { m_Page = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the control should be clipped to its bounds while rendering.
|
||||
/// </summary>
|
||||
protected override bool ShouldClip
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TabButton"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public TabButton(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
DragAndDrop_SetPackage(true, "TabButtonMove");
|
||||
Alignment = Pos.Top | Pos.Left;
|
||||
TextPadding = new Padding(5, 3, 3, 3);
|
||||
Padding = Padding.Two;
|
||||
KeyboardInputEnabled = true;
|
||||
}
|
||||
|
||||
public override void DragAndDrop_StartDragging(DragDrop.Package package, int x, int y)
|
||||
{
|
||||
IsHidden = true;
|
||||
}
|
||||
|
||||
public override void DragAndDrop_EndDragging(bool success, int x, int y)
|
||||
{
|
||||
IsHidden = false;
|
||||
IsDepressed = false;
|
||||
}
|
||||
|
||||
public override bool DragAndDrop_ShouldStartDrag()
|
||||
{
|
||||
return m_Control.AllowReorder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawTabButton(this, IsActive, m_Control.TabStrip.Dock);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Down Arrow keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyDown(bool down)
|
||||
{
|
||||
OnKeyRight(down);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Up Arrow keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyUp(bool down)
|
||||
{
|
||||
OnKeyLeft(down);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Right Arrow keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyRight(bool down)
|
||||
{
|
||||
if (down)
|
||||
{
|
||||
var count = Parent.Children.Count;
|
||||
int me = Parent.Children.IndexOf(this);
|
||||
if (me + 1 < count)
|
||||
{
|
||||
var nextTab = Parent.Children[me + 1];
|
||||
TabControl.OnTabPressed(nextTab);
|
||||
InputHandler.KeyboardFocus = nextTab;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Left Arrow keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyLeft(bool down)
|
||||
{
|
||||
if (down)
|
||||
{
|
||||
var count = Parent.Children.Count;
|
||||
int me = Parent.Children.IndexOf(this);
|
||||
if (me - 1 >= 0)
|
||||
{
|
||||
var prevTab = Parent.Children[me - 1];
|
||||
TabControl.OnTabPressed(prevTab);
|
||||
InputHandler.KeyboardFocus = prevTab;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates control colors.
|
||||
/// </summary>
|
||||
public override void UpdateColors()
|
||||
{
|
||||
if (IsActive)
|
||||
{
|
||||
if (IsDisabled)
|
||||
{
|
||||
TextColor = Skin.Colors.Tab.Active.Disabled;
|
||||
return;
|
||||
}
|
||||
if (IsDepressed)
|
||||
{
|
||||
TextColor = Skin.Colors.Tab.Active.Down;
|
||||
return;
|
||||
}
|
||||
if (IsHovered)
|
||||
{
|
||||
TextColor = Skin.Colors.Tab.Active.Hover;
|
||||
return;
|
||||
}
|
||||
|
||||
TextColor = Skin.Colors.Tab.Active.Normal;
|
||||
}
|
||||
|
||||
if (IsDisabled)
|
||||
{
|
||||
TextColor = Skin.Colors.Tab.Inactive.Disabled;
|
||||
return;
|
||||
}
|
||||
if (IsDepressed)
|
||||
{
|
||||
TextColor = Skin.Colors.Tab.Inactive.Down;
|
||||
return;
|
||||
}
|
||||
if (IsHovered)
|
||||
{
|
||||
TextColor = Skin.Colors.Tab.Inactive.Hover;
|
||||
return;
|
||||
}
|
||||
|
||||
TextColor = Skin.Colors.Tab.Inactive.Normal;
|
||||
}
|
||||
}
|
||||
}
|
250
Gwen/Control/TabControl.cs
Normal file
250
Gwen/Control/TabControl.cs
Normal file
|
@ -0,0 +1,250 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Control with multiple tabs that can be reordered and dragged.
|
||||
/// </summary>
|
||||
public class TabControl : Base
|
||||
{
|
||||
private readonly TabStrip m_TabStrip;
|
||||
private readonly ScrollBarButton[] m_Scroll;
|
||||
private TabButton m_CurrentButton;
|
||||
private int m_ScrollOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a tab has been added.
|
||||
/// </summary>
|
||||
public event GwenEventHandler TabAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a tab has been removed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler TabRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if tabs can be reordered by dragging.
|
||||
/// </summary>
|
||||
public bool AllowReorder { get { return m_TabStrip.AllowReorder; } set { m_TabStrip.AllowReorder = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Currently active tab button.
|
||||
/// </summary>
|
||||
public TabButton CurrentButton { get { return m_CurrentButton; } }
|
||||
|
||||
/// <summary>
|
||||
/// Current tab strip position.
|
||||
/// </summary>
|
||||
public Pos TabStripPosition { get { return m_TabStrip.StripPosition; }set { m_TabStrip.StripPosition = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Tab strip.
|
||||
/// </summary>
|
||||
public TabStrip TabStrip { get { return m_TabStrip; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TabControl"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public TabControl(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_Scroll = new ScrollBarButton[2];
|
||||
m_ScrollOffset = 0;
|
||||
|
||||
m_TabStrip = new TabStrip(this);
|
||||
m_TabStrip.StripPosition = Pos.Top;
|
||||
|
||||
// Make this some special control?
|
||||
m_Scroll[0] = new ScrollBarButton(this);
|
||||
m_Scroll[0].SetDirectionLeft();
|
||||
m_Scroll[0].Clicked += ScrollPressedLeft;
|
||||
m_Scroll[0].SetSize(14, 16);
|
||||
|
||||
m_Scroll[1] = new ScrollBarButton(this);
|
||||
m_Scroll[1].SetDirectionRight();
|
||||
m_Scroll[1].Clicked += ScrollPressedRight;
|
||||
m_Scroll[1].SetSize(14, 16);
|
||||
|
||||
m_InnerPanel = new TabControlInner(this);
|
||||
m_InnerPanel.Dock = Pos.Fill;
|
||||
m_InnerPanel.SendToBack();
|
||||
|
||||
IsTabable = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new page/tab.
|
||||
/// </summary>
|
||||
/// <param name="label">Tab label.</param>
|
||||
/// <param name="page">Page contents.</param>
|
||||
/// <returns>Newly created control.</returns>
|
||||
public TabButton AddPage(String label, Base page = null)
|
||||
{
|
||||
if (null == page)
|
||||
{
|
||||
page = new Base(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
page.Parent = this;
|
||||
}
|
||||
|
||||
TabButton button = new TabButton(m_TabStrip);
|
||||
button.SetText(label);
|
||||
button.Page = page;
|
||||
button.IsTabable = false;
|
||||
|
||||
AddPage(button);
|
||||
return button;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a page/tab.
|
||||
/// </summary>
|
||||
/// <param name="button">Page to add. (well, it's a TabButton which is a parent to the page).</param>
|
||||
public void AddPage(TabButton button)
|
||||
{
|
||||
Base page = button.Page;
|
||||
page.Parent = this;
|
||||
page.IsHidden = true;
|
||||
page.Margin = new Margin(6, 6, 6, 6);
|
||||
page.Dock = Pos.Fill;
|
||||
|
||||
button.Parent = m_TabStrip;
|
||||
button.Dock = Pos.Left;
|
||||
button.SizeToContents();
|
||||
if (button.TabControl != null)
|
||||
button.TabControl.UnsubscribeTabEvent(button);
|
||||
button.TabControl = this;
|
||||
button.Clicked += OnTabPressed;
|
||||
|
||||
if (null == m_CurrentButton)
|
||||
{
|
||||
button.Press();
|
||||
}
|
||||
|
||||
if (TabAdded != null)
|
||||
TabAdded.Invoke(this);
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
private void UnsubscribeTabEvent(TabButton button)
|
||||
{
|
||||
button.Clicked -= OnTabPressed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for tab selection.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source (TabButton).</param>
|
||||
internal virtual void OnTabPressed(Base control)
|
||||
{
|
||||
TabButton button = control as TabButton;
|
||||
if (null == button) return;
|
||||
|
||||
Base page = button.Page;
|
||||
if (null == page) return;
|
||||
|
||||
if (m_CurrentButton == button)
|
||||
return;
|
||||
|
||||
if (null != m_CurrentButton)
|
||||
{
|
||||
Base page2 = m_CurrentButton.Page;
|
||||
if (page2 != null)
|
||||
{
|
||||
page2.IsHidden = true;
|
||||
}
|
||||
m_CurrentButton.Redraw();
|
||||
m_CurrentButton = null;
|
||||
}
|
||||
|
||||
m_CurrentButton = button;
|
||||
|
||||
page.IsHidden = false;
|
||||
|
||||
m_TabStrip.Invalidate();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function invoked after layout.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void PostLayout(Skin.Base skin)
|
||||
{
|
||||
base.PostLayout(skin);
|
||||
HandleOverflow();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for tab removing.
|
||||
/// </summary>
|
||||
/// <param name="button"></param>
|
||||
internal virtual void OnLoseTab(TabButton button)
|
||||
{
|
||||
if (m_CurrentButton == button)
|
||||
m_CurrentButton = null;
|
||||
|
||||
//TODO: Select a tab if any exist.
|
||||
|
||||
if (TabRemoved != null)
|
||||
TabRemoved.Invoke(this);
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of tabs in the control.
|
||||
/// </summary>
|
||||
public int TabCount { get { return m_TabStrip.Children.Count; } }
|
||||
|
||||
private void HandleOverflow()
|
||||
{
|
||||
Point TabsSize = m_TabStrip.GetChildrenSize();
|
||||
|
||||
// Only enable the scrollers if the tabs are at the top.
|
||||
// This is a limitation we should explore.
|
||||
// Really TabControl should have derivitives for tabs placed elsewhere where we could specialize
|
||||
// some functions like this for each direction.
|
||||
bool needed = TabsSize.X > Width && m_TabStrip.Dock == Pos.Top;
|
||||
|
||||
m_Scroll[0].IsHidden = !needed;
|
||||
m_Scroll[1].IsHidden = !needed;
|
||||
|
||||
if (!needed) return;
|
||||
|
||||
m_ScrollOffset = Util.Clamp(m_ScrollOffset, 0, TabsSize.X - Width + 32);
|
||||
|
||||
#if false
|
||||
//
|
||||
// This isn't frame rate independent.
|
||||
// Could be better. Get rid of m_ScrollOffset and just use m_TabStrip.GetMargin().left ?
|
||||
// Then get a margin animation type and do it properly!
|
||||
// TODO!
|
||||
//
|
||||
m_TabStrip.SetMargin( Margin( Gwen::Approach( m_TabStrip.GetMargin().left, m_iScrollOffset * -1, 2 ), 0, 0, 0 ) );
|
||||
InvalidateParent();
|
||||
#else
|
||||
m_TabStrip.Margin = new Margin(m_ScrollOffset*-1, 0, 0, 0);
|
||||
#endif
|
||||
|
||||
m_Scroll[0].SetPosition(Width - 30, 5);
|
||||
m_Scroll[1].SetPosition(m_Scroll[0].Right, 5);
|
||||
}
|
||||
|
||||
protected virtual void ScrollPressedLeft(Base control)
|
||||
{
|
||||
m_ScrollOffset -= 120;
|
||||
}
|
||||
|
||||
protected virtual void ScrollPressedRight(Base control)
|
||||
{
|
||||
m_ScrollOffset += 120;
|
||||
}
|
||||
}
|
||||
}
|
204
Gwen/Control/TabStrip.cs
Normal file
204
Gwen/Control/TabStrip.cs
Normal file
|
@ -0,0 +1,204 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.ControlInternal;
|
||||
using Gwen.DragDrop;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Tab strip - groups TabButtons and allows reordering.
|
||||
/// </summary>
|
||||
public class TabStrip : Base
|
||||
{
|
||||
private Base m_TabDragControl;
|
||||
private bool m_AllowReorder;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether it is possible to reorder tabs by mouse dragging.
|
||||
/// </summary>
|
||||
public bool AllowReorder { get { return m_AllowReorder; } set { m_AllowReorder = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the control should be clipped to its bounds while rendering.
|
||||
/// </summary>
|
||||
protected override bool ShouldClip
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TabStrip"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public TabStrip(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_AllowReorder = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Strip position (top/left/right/bottom).
|
||||
/// </summary>
|
||||
public Pos StripPosition
|
||||
{
|
||||
get { return Dock; }
|
||||
set
|
||||
{
|
||||
Dock = value;
|
||||
if (Dock == Pos.Top)
|
||||
Padding = new Padding(5, 0, 0, 0);
|
||||
if (Dock == Pos.Left)
|
||||
Padding = new Padding(0, 5, 0, 0);
|
||||
if (Dock == Pos.Bottom)
|
||||
Padding = new Padding(5, 0, 0, 0);
|
||||
if (Dock == Pos.Right)
|
||||
Padding = new Padding(0, 5, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool DragAndDrop_HandleDrop(Package p, int x, int y)
|
||||
{
|
||||
Point LocalPos = CanvasPosToLocal(new Point(x, y));
|
||||
|
||||
TabButton button = DragAndDrop.SourceControl as TabButton;
|
||||
TabControl tabControl = Parent as TabControl;
|
||||
if (tabControl != null && button != null)
|
||||
{
|
||||
if (button.TabControl != tabControl)
|
||||
{
|
||||
// We've moved tab controls!
|
||||
tabControl.AddPage(button);
|
||||
}
|
||||
}
|
||||
|
||||
Base droppedOn = GetControlAt(LocalPos.X, LocalPos.Y);
|
||||
if (droppedOn != null)
|
||||
{
|
||||
Point dropPos = droppedOn.CanvasPosToLocal(new Point(x, y));
|
||||
DragAndDrop.SourceControl.BringNextToControl(droppedOn, dropPos.X > droppedOn.Width/2);
|
||||
}
|
||||
else
|
||||
{
|
||||
DragAndDrop.SourceControl.BringToFront();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DragAndDrop_CanAcceptPackage(Package p)
|
||||
{
|
||||
if (!m_AllowReorder)
|
||||
return false;
|
||||
|
||||
if (p.Name == "TabButtonMove")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
Point largestTab = new Point(5, 5);
|
||||
|
||||
int num = 0;
|
||||
foreach (var child in Children)
|
||||
{
|
||||
TabButton button = child as TabButton;
|
||||
if (null == button) continue;
|
||||
|
||||
button.SizeToContents();
|
||||
|
||||
Margin m = new Margin();
|
||||
int notFirst = num > 0 ? -1 : 0;
|
||||
|
||||
if (Dock == Pos.Top)
|
||||
{
|
||||
m.Left = notFirst;
|
||||
button.Dock = Pos.Left;
|
||||
}
|
||||
|
||||
if (Dock == Pos.Left)
|
||||
{
|
||||
m.Top = notFirst;
|
||||
button.Dock = Pos.Top;
|
||||
}
|
||||
|
||||
if (Dock == Pos.Right)
|
||||
{
|
||||
m.Top = notFirst;
|
||||
button.Dock = Pos.Top;
|
||||
}
|
||||
|
||||
if (Dock == Pos.Bottom)
|
||||
{
|
||||
m.Left = notFirst;
|
||||
button.Dock = Pos.Left;
|
||||
}
|
||||
|
||||
largestTab.X = Math.Max(largestTab.X, button.Width);
|
||||
largestTab.Y = Math.Max(largestTab.Y, button.Height);
|
||||
|
||||
button.Margin = m;
|
||||
num++;
|
||||
}
|
||||
|
||||
if (Dock == Pos.Top || Dock == Pos.Bottom)
|
||||
SetSize(Width, largestTab.Y);
|
||||
|
||||
if (Dock == Pos.Left || Dock == Pos.Right)
|
||||
SetSize(largestTab.X, Height);
|
||||
|
||||
base.Layout(skin);
|
||||
}
|
||||
|
||||
public override void DragAndDrop_HoverEnter(Package p, int x, int y)
|
||||
{
|
||||
if (m_TabDragControl != null)
|
||||
{
|
||||
throw new InvalidOperationException("ERROR! TabStrip::DragAndDrop_HoverEnter");
|
||||
}
|
||||
|
||||
m_TabDragControl = new Highlight(this);
|
||||
m_TabDragControl.MouseInputEnabled = false;
|
||||
m_TabDragControl.SetSize(3, Height);
|
||||
}
|
||||
|
||||
public override void DragAndDrop_HoverLeave(Package p)
|
||||
{
|
||||
if (m_TabDragControl != null)
|
||||
{
|
||||
RemoveChild(m_TabDragControl, false); // [omeg] need to do that explicitely
|
||||
m_TabDragControl.Dispose();
|
||||
}
|
||||
m_TabDragControl = null;
|
||||
}
|
||||
|
||||
public override void DragAndDrop_Hover(Package p, int x, int y)
|
||||
{
|
||||
Point localPos = CanvasPosToLocal(new Point(x, y));
|
||||
|
||||
Base droppedOn = GetControlAt(localPos.X, localPos.Y);
|
||||
if (droppedOn != null && droppedOn != this)
|
||||
{
|
||||
Point dropPos = droppedOn.CanvasPosToLocal(new Point(x, y));
|
||||
m_TabDragControl.SetBounds(new Rectangle(0, 0, 3, Height));
|
||||
m_TabDragControl.BringToFront();
|
||||
m_TabDragControl.SetPosition(droppedOn.X - 1, 0);
|
||||
|
||||
if (dropPos.X > droppedOn.Width/2)
|
||||
{
|
||||
m_TabDragControl.MoveBy(droppedOn.Width - 1, 0);
|
||||
}
|
||||
m_TabDragControl.Dock = Pos.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_TabDragControl.Dock = Pos.Left;
|
||||
m_TabDragControl.BringToFront();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
Gwen/Control/TabTitleBar.cs
Normal file
41
Gwen/Control/TabTitleBar.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using Gwen.DragDrop;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Titlebar for DockedTabControl.
|
||||
/// </summary>
|
||||
public class TabTitleBar : Label
|
||||
{
|
||||
public TabTitleBar(Base parent) : base(parent)
|
||||
{
|
||||
MouseInputEnabled = true;
|
||||
TextPadding = new Padding(5, 2, 5, 2);
|
||||
Padding = new Padding(1, 2, 1, 2);
|
||||
|
||||
DragAndDrop_SetPackage(true, "TabWindowMove");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawTabTitleBar(this);
|
||||
}
|
||||
|
||||
public override void DragAndDrop_StartDragging(Package package, int x, int y)
|
||||
{
|
||||
DragAndDrop.SourceControl = Parent;
|
||||
DragAndDrop.SourceControl.DragAndDrop_StartDragging(package, x, y);
|
||||
}
|
||||
|
||||
public void UpdateFromTab(TabButton button)
|
||||
{
|
||||
Text = button.Text;
|
||||
SizeToContents();
|
||||
}
|
||||
}
|
||||
}
|
607
Gwen/Control/TextBox.cs
Normal file
607
Gwen/Control/TextBox.cs
Normal file
|
@ -0,0 +1,607 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.Input;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Text box (editable).
|
||||
/// </summary>
|
||||
public class TextBox : Label
|
||||
{
|
||||
private bool m_SelectAll;
|
||||
|
||||
private int m_CursorPos;
|
||||
private int m_CursorEnd;
|
||||
|
||||
private Rectangle m_SelectionBounds;
|
||||
private Rectangle m_CaretBounds;
|
||||
|
||||
private float m_LastInputTime;
|
||||
|
||||
protected override bool AccelOnlyFocus { get { return true; } }
|
||||
protected override bool NeedsInputChars { get { return true; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether text should be selected when the control is focused.
|
||||
/// </summary>
|
||||
public bool SelectAllOnFocus { get { return m_SelectAll; } set { m_SelectAll = value; if (value) OnSelectAll(this); } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the text has active selection.
|
||||
/// </summary>
|
||||
public bool HasSelection { get { return m_CursorPos != m_CursorEnd; } }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the text has changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler TextChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the submit key has been pressed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler SubmitPressed;
|
||||
|
||||
/// <summary>
|
||||
/// Current cursor position (character index).
|
||||
/// </summary>
|
||||
public int CursorPos
|
||||
{
|
||||
get { return m_CursorPos; }
|
||||
set
|
||||
{
|
||||
if (m_CursorPos == value) return;
|
||||
|
||||
m_CursorPos = value;
|
||||
RefreshCursorBounds();
|
||||
}
|
||||
}
|
||||
|
||||
public int CursorEnd
|
||||
{
|
||||
get { return m_CursorEnd; }
|
||||
set
|
||||
{
|
||||
if (m_CursorEnd == value) return;
|
||||
|
||||
m_CursorEnd = value;
|
||||
RefreshCursorBounds();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the control can insert text at a given cursor position.
|
||||
/// </summary>
|
||||
/// <param name="text">Text to check.</param>
|
||||
/// <param name="position">Cursor position.</param>
|
||||
/// <returns>True if allowed.</returns>
|
||||
protected virtual bool IsTextAllowed(String text, int position)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TextBox"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public TextBox(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetSize(200, 20);
|
||||
|
||||
MouseInputEnabled = true;
|
||||
KeyboardInputEnabled = true;
|
||||
|
||||
Alignment = Pos.Left | Pos.CenterV;
|
||||
TextPadding = new Padding(4, 2, 4, 2);
|
||||
|
||||
m_CursorPos = 0;
|
||||
m_CursorEnd = 0;
|
||||
m_SelectAll = false;
|
||||
|
||||
TextColor = Color.FromArgb(255, 50, 50, 50); // TODO: From Skin
|
||||
|
||||
IsTabable = true;
|
||||
|
||||
AddAccelerator("Ctrl + C", OnCopy);
|
||||
AddAccelerator("Ctrl + X", OnCut);
|
||||
AddAccelerator("Ctrl + V", OnPaste);
|
||||
AddAccelerator("Ctrl + A", OnSelectAll);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the focus overlay.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void RenderFocus(Skin.Base skin)
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for text changed event.
|
||||
/// </summary>
|
||||
protected override void OnTextChanged()
|
||||
{
|
||||
base.OnTextChanged();
|
||||
|
||||
if (m_CursorPos > TextLength) m_CursorPos = TextLength;
|
||||
if (m_CursorEnd > TextLength) m_CursorEnd = TextLength;
|
||||
|
||||
if (TextChanged != null)
|
||||
TextChanged.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for character input event.
|
||||
/// </summary>
|
||||
/// <param name="chr">Character typed.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnChar(char chr)
|
||||
{
|
||||
base.OnChar(chr);
|
||||
|
||||
if (chr == '\t') return false;
|
||||
|
||||
InsertText(chr.ToString());
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts text at current cursor position, erasing selection if any.
|
||||
/// </summary>
|
||||
/// <param name="text">Text to insert.</param>
|
||||
void InsertText(String text)
|
||||
{
|
||||
// TODO: Make sure fits (implement maxlength)
|
||||
|
||||
if (HasSelection)
|
||||
{
|
||||
EraseSelection();
|
||||
}
|
||||
|
||||
if (m_CursorPos > TextLength)
|
||||
m_CursorPos = TextLength;
|
||||
|
||||
if (!IsTextAllowed(text, m_CursorPos))
|
||||
return;
|
||||
|
||||
String str = Text;
|
||||
str = str.Insert(m_CursorPos, text);
|
||||
SetText(str);
|
||||
|
||||
m_CursorPos += text.Length;
|
||||
m_CursorEnd = m_CursorPos;
|
||||
|
||||
RefreshCursorBounds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
base.Render(skin);
|
||||
|
||||
if (ShouldDrawBackground)
|
||||
skin.DrawTextBox(this);
|
||||
|
||||
if (!HasFocus) return;
|
||||
|
||||
// Draw selection.. if selected..
|
||||
if (m_CursorPos != m_CursorEnd)
|
||||
{
|
||||
skin.Renderer.DrawColor = Color.FromArgb(200, 50, 170, 255);
|
||||
skin.Renderer.DrawFilledRect(m_SelectionBounds);
|
||||
}
|
||||
|
||||
// Draw caret
|
||||
float time = Platform.Neutral.GetTimeInSeconds() - m_LastInputTime;
|
||||
|
||||
if ((time % 1.0f) <= 0.5f)
|
||||
{
|
||||
skin.Renderer.DrawColor = Color.Black;
|
||||
skin.Renderer.DrawFilledRect(m_CaretBounds);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void RefreshCursorBounds()
|
||||
{
|
||||
m_LastInputTime = Platform.Neutral.GetTimeInSeconds();
|
||||
|
||||
MakeCaretVisible();
|
||||
|
||||
Point pA = GetCharacterPosition(m_CursorPos);
|
||||
Point pB = GetCharacterPosition(m_CursorEnd);
|
||||
|
||||
m_SelectionBounds.X = Math.Min(pA.X, pB.X);
|
||||
m_SelectionBounds.Y = TextY - 1;
|
||||
m_SelectionBounds.Width = Math.Max(pA.X, pB.X) - m_SelectionBounds.X;
|
||||
m_SelectionBounds.Height = TextHeight + 2;
|
||||
|
||||
m_CaretBounds.X = pA.X;
|
||||
m_CaretBounds.Y = TextY - 1;
|
||||
m_CaretBounds.Width = 1;
|
||||
m_CaretBounds.Height = TextHeight + 2;
|
||||
|
||||
Redraw();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Paste event.
|
||||
/// </summary>
|
||||
/// <param name="from">Source control.</param>
|
||||
protected override void OnPaste(Base from)
|
||||
{
|
||||
base.OnPaste(from);
|
||||
InsertText(Platform.Neutral.GetClipboardText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Copy event.
|
||||
/// </summary>
|
||||
/// <param name="from">Source control.</param>
|
||||
protected override void OnCopy(Base from)
|
||||
{
|
||||
if (!HasSelection) return;
|
||||
base.OnCopy(from);
|
||||
|
||||
Platform.Neutral.SetClipboardText(GetSelection());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Cut event.
|
||||
/// </summary>
|
||||
/// <param name="from">Source control.</param>
|
||||
protected override void OnCut(Base from)
|
||||
{
|
||||
if (!HasSelection) return;
|
||||
base.OnCut(from);
|
||||
|
||||
Platform.Neutral.SetClipboardText(GetSelection());
|
||||
EraseSelection();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Select All event.
|
||||
/// </summary>
|
||||
/// <param name="from">Source control.</param>
|
||||
protected override void OnSelectAll(Base from)
|
||||
{
|
||||
//base.OnSelectAll(from);
|
||||
m_CursorEnd = 0;
|
||||
m_CursorPos = TextLength;
|
||||
|
||||
RefreshCursorBounds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse double click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
protected override void OnMouseDoubleClickedLeft(int x, int y)
|
||||
{
|
||||
//base.OnMouseDoubleClickedLeft(x, y);
|
||||
OnSelectAll(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Return keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyReturn(bool down)
|
||||
{
|
||||
base.OnKeyReturn(down);
|
||||
if (down) return true;
|
||||
|
||||
OnReturn();
|
||||
|
||||
// Try to move to the next control, as if tab had been pressed
|
||||
OnKeyTab(true);
|
||||
|
||||
// If we still have focus, blur it.
|
||||
if (HasFocus)
|
||||
{
|
||||
Blur();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Backspace keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyBackspace(bool down)
|
||||
{
|
||||
base.OnKeyBackspace(down);
|
||||
|
||||
if (!down) return true;
|
||||
if (HasSelection)
|
||||
{
|
||||
EraseSelection();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_CursorPos == 0) return true;
|
||||
|
||||
DeleteText(m_CursorPos - 1, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Delete keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyDelete(bool down)
|
||||
{
|
||||
base.OnKeyDelete(down);
|
||||
if (!down) return true;
|
||||
if (HasSelection)
|
||||
{
|
||||
EraseSelection();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_CursorPos >= TextLength) return true;
|
||||
|
||||
DeleteText(m_CursorPos, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Left Arrow keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyLeft(bool down)
|
||||
{
|
||||
base.OnKeyLeft(down);
|
||||
if (!down) return true;
|
||||
|
||||
if (m_CursorPos > 0)
|
||||
m_CursorPos--;
|
||||
|
||||
if (!Input.InputHandler.IsShiftDown)
|
||||
{
|
||||
m_CursorEnd = m_CursorPos;
|
||||
}
|
||||
|
||||
RefreshCursorBounds();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Right Arrow keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyRight(bool down)
|
||||
{
|
||||
base.OnKeyRight(down);
|
||||
if (!down) return true;
|
||||
|
||||
if (m_CursorPos < TextLength)
|
||||
m_CursorPos++;
|
||||
|
||||
if (!Input.InputHandler.IsShiftDown)
|
||||
{
|
||||
m_CursorEnd = m_CursorPos;
|
||||
}
|
||||
|
||||
RefreshCursorBounds();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for Home keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyHome(bool down)
|
||||
{
|
||||
base.OnKeyHome(down);
|
||||
if (!down) return true;
|
||||
m_CursorPos = 0;
|
||||
|
||||
if (!Input.InputHandler.IsShiftDown)
|
||||
{
|
||||
m_CursorEnd = m_CursorPos;
|
||||
}
|
||||
|
||||
RefreshCursorBounds();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for End keyboard event.
|
||||
/// </summary>
|
||||
/// <param name="down">Indicates whether the key was pressed or released.</param>
|
||||
/// <returns>
|
||||
/// True if handled.
|
||||
/// </returns>
|
||||
protected override bool OnKeyEnd(bool down)
|
||||
{
|
||||
base.OnKeyEnd(down);
|
||||
m_CursorPos = TextLength;
|
||||
|
||||
if (!Input.InputHandler.IsShiftDown)
|
||||
{
|
||||
m_CursorEnd = m_CursorPos;
|
||||
}
|
||||
|
||||
RefreshCursorBounds();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns currently selected text.
|
||||
/// </summary>
|
||||
/// <returns>Current selection.</returns>
|
||||
public String GetSelection()
|
||||
{
|
||||
if (!HasSelection) return String.Empty;
|
||||
|
||||
int start = Math.Min(m_CursorPos, m_CursorEnd);
|
||||
int end = Math.Max(m_CursorPos, m_CursorEnd);
|
||||
|
||||
String str = Text;
|
||||
return str.Substring(start, end - start);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes text.
|
||||
/// </summary>
|
||||
/// <param name="startPos">Starting cursor position.</param>
|
||||
/// <param name="length">Length in characters.</param>
|
||||
public virtual void DeleteText(int startPos, int length)
|
||||
{
|
||||
String str = Text;
|
||||
str = str.Remove(startPos, length);
|
||||
SetText(str);
|
||||
|
||||
if (m_CursorPos > startPos)
|
||||
{
|
||||
CursorPos = m_CursorPos - length;
|
||||
}
|
||||
|
||||
CursorEnd = m_CursorPos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes selected text.
|
||||
/// </summary>
|
||||
public virtual void EraseSelection()
|
||||
{
|
||||
int start = Math.Min(m_CursorPos, m_CursorEnd);
|
||||
int end = Math.Max(m_CursorPos, m_CursorEnd);
|
||||
|
||||
DeleteText(start, end - start);
|
||||
|
||||
// Move the cursor to the start of the selection,
|
||||
// since the end is probably outside of the string now.
|
||||
m_CursorPos = start;
|
||||
m_CursorEnd = start;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="down">If set to <c>true</c> mouse button is down.</param>
|
||||
protected override void OnMouseClickedLeft(int x, int y, bool down)
|
||||
{
|
||||
base.OnMouseClickedLeft(x, y, down);
|
||||
if (m_SelectAll)
|
||||
{
|
||||
OnSelectAll(this);
|
||||
//m_SelectAll = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int c = GetClosestCharacter(x, y);
|
||||
|
||||
if (down)
|
||||
{
|
||||
CursorPos = c;
|
||||
|
||||
if (!Input.InputHandler.IsShiftDown)
|
||||
CursorEnd = c;
|
||||
|
||||
InputHandler.MouseFocus = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (InputHandler.MouseFocus == this)
|
||||
{
|
||||
CursorPos = c;
|
||||
InputHandler.MouseFocus = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse moved event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="dx">X change.</param>
|
||||
/// <param name="dy">Y change.</param>
|
||||
protected override void OnMouseMoved(int x, int y, int dx, int dy)
|
||||
{
|
||||
base.OnMouseMoved(x, y, dx, dy);
|
||||
if (InputHandler.MouseFocus != this) return;
|
||||
|
||||
int c = GetClosestCharacter(x, y);
|
||||
|
||||
CursorPos = c;
|
||||
}
|
||||
|
||||
protected virtual void MakeCaretVisible()
|
||||
{
|
||||
int caretPos = GetCharacterPosition(m_CursorPos).X - TextX;
|
||||
|
||||
// If the caret is already in a semi-good position, leave it.
|
||||
{
|
||||
int realCaretPos = caretPos + TextX;
|
||||
if (realCaretPos > Width*0.1f && realCaretPos < Width*0.9f)
|
||||
return;
|
||||
}
|
||||
|
||||
// The ideal position is for the caret to be right in the middle
|
||||
int idealx = (int)(-caretPos + Width * 0.5f);
|
||||
|
||||
// Don't show too much whitespace to the right
|
||||
if (idealx + TextWidth < Width - TextPadding.Right)
|
||||
idealx = -TextWidth + (Width - TextPadding.Right);
|
||||
|
||||
// Or the left
|
||||
if (idealx > TextPadding.Left)
|
||||
idealx = TextPadding.Left;
|
||||
|
||||
SetTextPosition(idealx, TextY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
base.Layout(skin);
|
||||
|
||||
RefreshCursorBounds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the return key.
|
||||
/// </summary>
|
||||
protected virtual void OnReturn()
|
||||
{
|
||||
if (SubmitPressed != null)
|
||||
SubmitPressed.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
85
Gwen/Control/TextBoxNumeric.cs
Normal file
85
Gwen/Control/TextBoxNumeric.cs
Normal file
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Numeric text box - accepts only float numbers.
|
||||
/// </summary>
|
||||
public class TextBoxNumeric : TextBox
|
||||
{
|
||||
/// <summary>
|
||||
/// Current numeric value.
|
||||
/// </summary>
|
||||
protected float m_Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TextBoxNumeric"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public TextBoxNumeric(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetText("0", false);
|
||||
}
|
||||
|
||||
protected virtual bool IsTextAllowed(String str)
|
||||
{
|
||||
if (str == "" || str == "-")
|
||||
return true; // annoying if single - is not allowed
|
||||
float d;
|
||||
return float.TryParse(str, out d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the control can insert text at a given cursor position.
|
||||
/// </summary>
|
||||
/// <param name="text">Text to check.</param>
|
||||
/// <param name="position">Cursor position.</param>
|
||||
/// <returns>True if allowed.</returns>
|
||||
protected override bool IsTextAllowed(String text, int position)
|
||||
{
|
||||
String newText = Text.Insert(position, text);
|
||||
return IsTextAllowed(newText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current numerical value.
|
||||
/// </summary>
|
||||
public virtual float Value
|
||||
{
|
||||
get { return m_Value; }
|
||||
set
|
||||
{
|
||||
m_Value = value;
|
||||
Text = value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
// text -> value
|
||||
/// <summary>
|
||||
/// Handler for text changed event.
|
||||
/// </summary>
|
||||
protected override void OnTextChanged()
|
||||
{
|
||||
if (String.IsNullOrEmpty(Text) || Text == "-")
|
||||
{
|
||||
m_Value = 0;
|
||||
//SetText("0");
|
||||
}
|
||||
else
|
||||
m_Value = float.Parse(Text);
|
||||
base.OnTextChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the control text.
|
||||
/// </summary>
|
||||
/// <param name="str">Text to set.</param>
|
||||
/// <param name="doEvents">Determines whether to invoke "text changed" event.</param>
|
||||
public override void SetText(string str, bool doEvents = true)
|
||||
{
|
||||
if (IsTextAllowed(str))
|
||||
base.SetText(str, doEvents);
|
||||
}
|
||||
}
|
||||
}
|
41
Gwen/Control/TextBoxPassword.cs
Normal file
41
Gwen/Control/TextBoxPassword.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Text box with masked text.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class doesn't prevent programatic access to the text in any way.
|
||||
/// </remarks>
|
||||
public class TextBoxPassword : TextBox
|
||||
{
|
||||
private String m_Mask;
|
||||
private char m_MaskCharacter;
|
||||
|
||||
/// <summary>
|
||||
/// Character used in place of actual characters for display.
|
||||
/// </summary>
|
||||
public char MaskCharacter { get { return m_MaskCharacter; } set { m_MaskCharacter = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TextBoxPassword"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public TextBoxPassword(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_MaskCharacter = '*';
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for text changed event.
|
||||
/// </summary>
|
||||
protected override void OnTextChanged()
|
||||
{
|
||||
m_Mask = new string(MaskCharacter, Text.Length);
|
||||
TextOverride = m_Mask;
|
||||
base.OnTextChanged();
|
||||
}
|
||||
}
|
||||
}
|
95
Gwen/Control/TreeControl.cs
Normal file
95
Gwen/Control/TreeControl.cs
Normal file
|
@ -0,0 +1,95 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Tree control.
|
||||
/// </summary>
|
||||
public class TreeControl : TreeNode
|
||||
{
|
||||
private readonly ScrollControl m_ScrollControl;
|
||||
private bool m_MultiSelect;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if multiple nodes can be selected at the same time.
|
||||
/// </summary>
|
||||
public bool AllowMultiSelect { get { return m_MultiSelect; } set { m_MultiSelect = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TreeControl"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public TreeControl(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_TreeControl = this;
|
||||
|
||||
RemoveChild(m_ToggleButton, true);
|
||||
m_ToggleButton = null;
|
||||
RemoveChild(m_Title, true);
|
||||
m_Title = null;
|
||||
RemoveChild(m_InnerPanel, true);
|
||||
m_InnerPanel = null;
|
||||
|
||||
m_MultiSelect = false;
|
||||
|
||||
m_ScrollControl = new ScrollControl(this);
|
||||
m_ScrollControl.Dock = Pos.Fill;
|
||||
m_ScrollControl.EnableScroll(false, true);
|
||||
m_ScrollControl.AutoHideBars = true;
|
||||
m_ScrollControl.Margin = Margin.One;
|
||||
|
||||
m_InnerPanel = m_ScrollControl;
|
||||
|
||||
m_ScrollControl.SetInnerSize(1000, 1000); // todo: why such arbitrary numbers?
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
if (ShouldDrawBackground)
|
||||
skin.DrawTreeControl(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked when control children's bounds change.
|
||||
/// </summary>
|
||||
/// <param name="oldChildBounds"></param>
|
||||
/// <param name="child"></param>
|
||||
protected override void OnChildBoundsChanged(System.Drawing.Rectangle oldChildBounds, Base child)
|
||||
{
|
||||
if (m_ScrollControl != null)
|
||||
m_ScrollControl.UpdateScrollBars();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all child nodes.
|
||||
/// </summary>
|
||||
public virtual void RemoveAll()
|
||||
{
|
||||
m_ScrollControl.DeleteAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for node added event.
|
||||
/// </summary>
|
||||
/// <param name="node">Node added.</param>
|
||||
public virtual void OnNodeAdded(TreeNode node)
|
||||
{
|
||||
node.LabelPressed += OnNodeSelected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for node selected event.
|
||||
/// </summary>
|
||||
/// <param name="Control">Node selected.</param>
|
||||
protected virtual void OnNodeSelected(Base Control)
|
||||
{
|
||||
if (!m_MultiSelect /*|| InputHandler.InputHandler.IsKeyDown(Key.Control)*/)
|
||||
UnselectAll();
|
||||
}
|
||||
}
|
||||
}
|
326
Gwen/Control/TreeNode.cs
Normal file
326
Gwen/Control/TreeNode.cs
Normal file
|
@ -0,0 +1,326 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Tree control node.
|
||||
/// </summary>
|
||||
public class TreeNode : Base
|
||||
{
|
||||
public const int TreeIndentation = 14;
|
||||
|
||||
protected TreeControl m_TreeControl;
|
||||
protected Button m_ToggleButton;
|
||||
protected Button m_Title;
|
||||
private bool m_Root;
|
||||
private bool m_Selected;
|
||||
private bool m_Selectable;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this is a root node.
|
||||
/// </summary>
|
||||
public bool IsRoot { get { return m_Root; } set { m_Root = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Parent tree control.
|
||||
/// </summary>
|
||||
public TreeControl TreeControl { get { return m_TreeControl; } set { m_TreeControl = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the node is selectable.
|
||||
/// </summary>
|
||||
public bool IsSelectable { get { return m_Selectable; } set { m_Selectable = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the node is selected.
|
||||
/// </summary>
|
||||
public bool IsSelected
|
||||
{
|
||||
get { return m_Selected; }
|
||||
set
|
||||
{
|
||||
if (!IsSelectable)
|
||||
return;
|
||||
if (IsSelected == value)
|
||||
return;
|
||||
|
||||
m_Selected = value;
|
||||
|
||||
if (m_Title != null)
|
||||
m_Title.ToggleState = value;
|
||||
|
||||
if (SelectionChanged != null)
|
||||
SelectionChanged.Invoke(this);
|
||||
|
||||
// propagate to root parent (tree)
|
||||
if (m_TreeControl != null && m_TreeControl.SelectionChanged != null)
|
||||
m_TreeControl.SelectionChanged.Invoke(this);
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (Selected != null)
|
||||
Selected.Invoke(this);
|
||||
|
||||
if (m_TreeControl != null && m_TreeControl.Selected != null)
|
||||
m_TreeControl.Selected.Invoke(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Unselected != null)
|
||||
Unselected.Invoke(this);
|
||||
|
||||
if (m_TreeControl != null && m_TreeControl.Unselected != null)
|
||||
m_TreeControl.Unselected.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Node's label.
|
||||
/// </summary>
|
||||
public String Text { get { return m_Title.Text; } set { m_Title.Text = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the node label has been pressed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler LabelPressed;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the node's selected state has changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler SelectionChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the node has been selected.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Selected;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the node has been unselected.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Unselected;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the node has been expanded.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Expanded;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the node has been collapsed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Collapsed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TreeNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public TreeNode(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_ToggleButton = new TreeToggleButton(this);
|
||||
m_ToggleButton.SetBounds(0, 0, 15, 15);
|
||||
m_ToggleButton.Toggled += OnToggleButtonPress;
|
||||
|
||||
m_Title = new TreeNodeLabel(this);
|
||||
m_Title.Dock = Pos.Top;
|
||||
m_Title.Margin = new Margin(16, 0, 0, 0);
|
||||
m_Title.DoubleClickedLeft += OnDoubleClickName;
|
||||
m_Title.Clicked += OnClickName;
|
||||
|
||||
m_InnerPanel = new Base(this);
|
||||
m_InnerPanel.Dock = Pos.Top;
|
||||
m_InnerPanel.Height = 100;
|
||||
m_InnerPanel.Margin = new Margin(TreeIndentation, 1, 0, 0);
|
||||
m_InnerPanel.Hide();
|
||||
|
||||
m_Root = false;
|
||||
m_Selected = false;
|
||||
m_Selectable = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
int bottom = 0;
|
||||
if (m_InnerPanel.Children.Count > 0)
|
||||
{
|
||||
bottom = m_InnerPanel.Children.Last().Y + m_InnerPanel.Y;
|
||||
}
|
||||
|
||||
skin.DrawTreeNode(this, m_InnerPanel.IsVisible, IsSelected, m_Title.Height, m_Title.TextRight,
|
||||
(int)(m_ToggleButton.Y + m_ToggleButton.Height * 0.5f), bottom, m_TreeControl == Parent); // IsRoot
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
if (m_ToggleButton != null)
|
||||
{
|
||||
if (m_Title != null)
|
||||
{
|
||||
m_ToggleButton.SetPosition(0, (m_Title.Height - m_ToggleButton.Height)*0.5f);
|
||||
}
|
||||
|
||||
if (m_InnerPanel.Children.Count == 0)
|
||||
{
|
||||
m_ToggleButton.Hide();
|
||||
m_ToggleButton.ToggleState = false;
|
||||
m_InnerPanel.Hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ToggleButton.Show();
|
||||
m_InnerPanel.SizeToChildren(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
base.Layout(skin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function invoked after layout.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void PostLayout(Skin.Base skin)
|
||||
{
|
||||
if (SizeToChildren(false, true))
|
||||
{
|
||||
InvalidateParent();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new child node.
|
||||
/// </summary>
|
||||
/// <param name="label">Node's label.</param>
|
||||
/// <returns>Newly created control.</returns>
|
||||
public TreeNode AddNode(string label)
|
||||
{
|
||||
TreeNode node = new TreeNode(this);
|
||||
node.Text = label;
|
||||
node.Dock = Pos.Top;
|
||||
node.IsRoot = this is TreeControl;
|
||||
node.TreeControl = m_TreeControl;
|
||||
|
||||
if (m_TreeControl != null)
|
||||
{
|
||||
m_TreeControl.OnNodeAdded(node);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the node.
|
||||
/// </summary>
|
||||
public void Open()
|
||||
{
|
||||
m_InnerPanel.Show();
|
||||
if (m_ToggleButton != null)
|
||||
m_ToggleButton.ToggleState = true;
|
||||
|
||||
if (Expanded != null)
|
||||
Expanded.Invoke(this);
|
||||
if (m_TreeControl != null && m_TreeControl.Expanded != null)
|
||||
m_TreeControl.Expanded.Invoke(this);
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the node.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
m_InnerPanel.Hide();
|
||||
if (m_ToggleButton != null)
|
||||
m_ToggleButton.ToggleState = false;
|
||||
|
||||
if (Collapsed != null)
|
||||
Collapsed.Invoke(this);
|
||||
if (m_TreeControl != null && m_TreeControl.Collapsed != null)
|
||||
m_TreeControl.Collapsed.Invoke(this);
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the node and all child nodes.
|
||||
/// </summary>
|
||||
public void ExpandAll()
|
||||
{
|
||||
Open();
|
||||
foreach (Base child in Children)
|
||||
{
|
||||
TreeNode node = child as TreeNode;
|
||||
if (node == null)
|
||||
continue;
|
||||
node.ExpandAll();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the selection on the node and all child nodes.
|
||||
/// </summary>
|
||||
public void UnselectAll()
|
||||
{
|
||||
IsSelected = false;
|
||||
if (m_Title != null)
|
||||
m_Title.ToggleState = false;
|
||||
|
||||
foreach (Base child in Children)
|
||||
{
|
||||
TreeNode node = child as TreeNode;
|
||||
if (node == null)
|
||||
continue;
|
||||
node.UnselectAll();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the toggle button.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source.</param>
|
||||
protected virtual void OnToggleButtonPress(Base control)
|
||||
{
|
||||
if (m_ToggleButton.ToggleState)
|
||||
{
|
||||
Open();
|
||||
}
|
||||
else
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for label double click.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source.</param>
|
||||
protected virtual void OnDoubleClickName(Base control)
|
||||
{
|
||||
if (!m_ToggleButton.IsVisible)
|
||||
return;
|
||||
m_ToggleButton.Toggle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for label click.
|
||||
/// </summary>
|
||||
/// <param name="control">Event source.</param>
|
||||
protected virtual void OnClickName(Base control)
|
||||
{
|
||||
if (LabelPressed != null)
|
||||
LabelPressed.Invoke(this);
|
||||
IsSelected = !IsSelected;
|
||||
}
|
||||
}
|
||||
}
|
193
Gwen/Control/VerticalScrollBar.cs
Normal file
193
Gwen/Control/VerticalScrollBar.cs
Normal file
|
@ -0,0 +1,193 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.Input;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Vertical scrollbar.
|
||||
/// </summary>
|
||||
public class VerticalScrollBar : ScrollBar
|
||||
{
|
||||
/// <summary>
|
||||
/// Bar size (in pixels).
|
||||
/// </summary>
|
||||
public override int BarSize
|
||||
{
|
||||
get { return m_Bar.Height; }
|
||||
set { m_Bar.Height = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bar position (in pixels).
|
||||
/// </summary>
|
||||
public override int BarPos
|
||||
{
|
||||
get { return m_Bar.Y - Width; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Button size (in pixels).
|
||||
/// </summary>
|
||||
public override int ButtonSize
|
||||
{
|
||||
get { return Width; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VerticalScrollBar"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public VerticalScrollBar(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_Bar.IsVertical = true;
|
||||
|
||||
m_ScrollButton[0].SetDirectionUp();
|
||||
m_ScrollButton[0].Clicked += NudgeUp;
|
||||
|
||||
m_ScrollButton[1].SetDirectionDown();
|
||||
m_ScrollButton[1].Clicked += NudgeDown;
|
||||
|
||||
m_Bar.Dragged += OnBarMoved;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
base.Layout(skin);
|
||||
|
||||
m_ScrollButton[0].Height = Width;
|
||||
m_ScrollButton[0].Dock = Pos.Top;
|
||||
|
||||
m_ScrollButton[1].Height = Width;
|
||||
m_ScrollButton[1].Dock = Pos.Bottom;
|
||||
|
||||
m_Bar.Width = ButtonSize;
|
||||
m_Bar.Padding = new Padding(0, ButtonSize, 0, ButtonSize);
|
||||
|
||||
float barHeight = 0.0f;
|
||||
if (m_ContentSize > 0.0f) barHeight = (m_ViewableContentSize/m_ContentSize)*(Height - (ButtonSize*2));
|
||||
|
||||
if (barHeight < ButtonSize*0.5f)
|
||||
barHeight = (int) (ButtonSize*0.5f);
|
||||
|
||||
m_Bar.Height = (int) (barHeight);
|
||||
m_Bar.IsHidden = Height - (ButtonSize*2) <= barHeight;
|
||||
|
||||
//Based on our last scroll amount, produce a position for the bar
|
||||
if (!m_Bar.IsHeld)
|
||||
{
|
||||
SetScrollAmount(ScrollAmount, true);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void NudgeUp(Base control)
|
||||
{
|
||||
if (!IsDisabled)
|
||||
SetScrollAmount(ScrollAmount - NudgeAmount, true);
|
||||
}
|
||||
|
||||
public virtual void NudgeDown(Base control)
|
||||
{
|
||||
if (!IsDisabled)
|
||||
SetScrollAmount(ScrollAmount + NudgeAmount, true);
|
||||
}
|
||||
|
||||
public override void ScrollToTop()
|
||||
{
|
||||
SetScrollAmount(0, true);
|
||||
}
|
||||
|
||||
public override void ScrollToBottom()
|
||||
{
|
||||
SetScrollAmount(1, true);
|
||||
}
|
||||
|
||||
public override float NudgeAmount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Depressed)
|
||||
return m_ViewableContentSize / m_ContentSize;
|
||||
else
|
||||
return base.NudgeAmount;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.NudgeAmount = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="down">If set to <c>true</c> mouse button is down.</param>
|
||||
protected override void OnMouseClickedLeft(int x, int y, bool down)
|
||||
{
|
||||
if (down)
|
||||
{
|
||||
m_Depressed = true;
|
||||
InputHandler.MouseFocus = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
Point clickPos = CanvasPosToLocal(new Point(x, y));
|
||||
if (clickPos.Y < m_Bar.Y)
|
||||
NudgeUp(this);
|
||||
else if (clickPos.Y > m_Bar.Y + m_Bar.Height)
|
||||
NudgeDown(this);
|
||||
|
||||
m_Depressed = false;
|
||||
InputHandler.MouseFocus = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override float CalculateScrolledAmount()
|
||||
{
|
||||
return (float)(m_Bar.Y - ButtonSize) / (Height - m_Bar.Height - (ButtonSize * 2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the scroll amount (0-1).
|
||||
/// </summary>
|
||||
/// <param name="value">Scroll amount.</param>
|
||||
/// <param name="forceUpdate">Determines whether the control should be updated.</param>
|
||||
/// <returns>True if control state changed.</returns>
|
||||
public override bool SetScrollAmount(float value, bool forceUpdate = false)
|
||||
{
|
||||
value = Util.Clamp(value, 0, 1);
|
||||
|
||||
if (!base.SetScrollAmount(value, forceUpdate))
|
||||
return false;
|
||||
|
||||
if (forceUpdate)
|
||||
{
|
||||
int newY = (int)(ButtonSize + (value * ((Height - m_Bar.Height) - (ButtonSize * 2))));
|
||||
m_Bar.MoveTo(m_Bar.X, newY);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the BarMoved event.
|
||||
/// </summary>
|
||||
/// <param name="control">The control.</param>
|
||||
protected override void OnBarMoved(Base control)
|
||||
{
|
||||
if (m_Bar.IsHeld)
|
||||
{
|
||||
SetScrollAmount(CalculateScrolledAmount(), false);
|
||||
base.OnBarMoved(control);
|
||||
}
|
||||
else
|
||||
InvalidateParent();
|
||||
}
|
||||
}
|
||||
}
|
63
Gwen/Control/VerticalSlider.cs
Normal file
63
Gwen/Control/VerticalSlider.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Vertical slider.
|
||||
/// </summary>
|
||||
public class VerticalSlider : Slider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VerticalSlider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public VerticalSlider(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_SliderBar.IsHorizontal = false;
|
||||
}
|
||||
|
||||
protected override float CalculateValue()
|
||||
{
|
||||
return 1 - m_SliderBar.Y / (float)(Height - m_SliderBar.Height);
|
||||
}
|
||||
|
||||
protected override void UpdateBarFromValue()
|
||||
{
|
||||
m_SliderBar.MoveTo(m_SliderBar.X, (int)((Height - m_SliderBar.Height) * (1 - m_Value)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="down">If set to <c>true</c> mouse button is down.</param>
|
||||
protected override void OnMouseClickedLeft(int x, int y, bool down)
|
||||
{
|
||||
m_SliderBar.MoveTo(m_SliderBar.X, (int) (CanvasPosToLocal(new Point(x, y)).Y - m_SliderBar.Height*0.5));
|
||||
m_SliderBar.InputMouseClickedLeft(x, y, down);
|
||||
OnMoved(m_SliderBar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
m_SliderBar.SetSize(Width, 15);
|
||||
UpdateBarFromValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawSlider(this, false, m_SnapToNotches ? m_NotchCount : 0, m_SliderBar.Height);
|
||||
}
|
||||
}
|
||||
}
|
221
Gwen/Control/VerticalSplitter.cs
Normal file
221
Gwen/Control/VerticalSplitter.cs
Normal file
|
@ -0,0 +1,221 @@
|
|||
using System;
|
||||
//using System.Windows.Forms;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
public class VerticalSplitter : Base
|
||||
{
|
||||
private readonly SplitterBar m_HSplitter;
|
||||
private readonly Base[] m_Sections;
|
||||
|
||||
private float m_HVal; // 0-1
|
||||
private int m_BarSize; // pixels
|
||||
private int m_ZoomedSection; // 0-3
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when one of the panels has been zoomed (maximized).
|
||||
/// </summary>
|
||||
public event GwenEventHandler PanelZoomed;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when one of the panels has been unzoomed (restored).
|
||||
/// </summary>
|
||||
public event GwenEventHandler PanelUnZoomed;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the zoomed panel has been changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler ZoomChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CrossSplitter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public VerticalSplitter(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_Sections = new Base[2];
|
||||
|
||||
m_HSplitter = new SplitterBar(this);
|
||||
m_HSplitter.SetPosition(128, 0);
|
||||
m_HSplitter.Dragged += OnHorizontalMoved;
|
||||
//m_HSplitter.Cursor = Cursors.SizeWE;
|
||||
|
||||
m_HVal = 0.5f;
|
||||
|
||||
SetPanel(0, null);
|
||||
SetPanel(1, null);
|
||||
|
||||
SplitterSize = 5;
|
||||
SplittersVisible = false;
|
||||
|
||||
m_ZoomedSection = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Centers the panels so that they take even amount of space.
|
||||
/// </summary>
|
||||
public void CenterPanels()
|
||||
{
|
||||
m_HVal = 0.5f;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public void SetHValue(float value)
|
||||
{
|
||||
if (value <= 1f || value >= 0)
|
||||
m_HVal = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether any of the panels is zoomed.
|
||||
/// </summary>
|
||||
public bool IsZoomed { get { return m_ZoomedSection != -1; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether splitters should be visible.
|
||||
/// </summary>
|
||||
public bool SplittersVisible
|
||||
{
|
||||
get { return m_HSplitter.ShouldDrawBackground; }
|
||||
set
|
||||
{
|
||||
m_HSplitter.ShouldDrawBackground = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the splitter.
|
||||
/// </summary>
|
||||
public int SplitterSize { get { return m_BarSize; } set { m_BarSize = value; } }
|
||||
|
||||
private void UpdateHSplitter()
|
||||
{
|
||||
m_HSplitter.MoveTo((Width - m_HSplitter.Width) * (m_HVal), m_HSplitter.Y);
|
||||
}
|
||||
|
||||
protected void OnHorizontalMoved(Base control)
|
||||
{
|
||||
m_HVal = CalculateValueHorizontal();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
private float CalculateValueHorizontal()
|
||||
{
|
||||
return m_HSplitter.X / (float)(Width - m_HSplitter.Width);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
m_HSplitter.SetSize(m_BarSize, Height);
|
||||
|
||||
UpdateHSplitter();
|
||||
|
||||
if (m_ZoomedSection == -1)
|
||||
{
|
||||
if (m_Sections[0] != null)
|
||||
m_Sections[0].SetBounds(0, 0, m_HSplitter.X, Height);
|
||||
|
||||
if (m_Sections[1] != null)
|
||||
m_Sections[1].SetBounds(m_HSplitter.X + m_BarSize, 0, Width - (m_HSplitter.X + m_BarSize), Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
//This should probably use Fill docking instead
|
||||
m_Sections[m_ZoomedSection].SetBounds(0, 0, Width, Height);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a control to the specific inner section.
|
||||
/// </summary>
|
||||
/// <param name="index">Section index (0-3).</param>
|
||||
/// <param name="panel">Control to assign.</param>
|
||||
public void SetPanel(int index, Base panel)
|
||||
{
|
||||
m_Sections[index] = panel;
|
||||
|
||||
if (panel != null)
|
||||
{
|
||||
panel.Dock = Pos.None;
|
||||
panel.Parent = this;
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specific inner section.
|
||||
/// </summary>
|
||||
/// <param name="index">Section index (0-3).</param>
|
||||
/// <returns>Specified section.</returns>
|
||||
public Base GetPanel(int index)
|
||||
{
|
||||
return m_Sections[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal handler for the zoom changed event.
|
||||
/// </summary>
|
||||
protected void OnZoomChanged()
|
||||
{
|
||||
if (ZoomChanged != null)
|
||||
ZoomChanged.Invoke(this);
|
||||
|
||||
if (m_ZoomedSection == -1)
|
||||
{
|
||||
if (PanelUnZoomed != null)
|
||||
PanelUnZoomed.Invoke(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PanelZoomed != null)
|
||||
PanelZoomed.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximizes the specified panel so it fills the entire control.
|
||||
/// </summary>
|
||||
/// <param name="section">Panel index (0-3).</param>
|
||||
public void Zoom(int section)
|
||||
{
|
||||
UnZoom();
|
||||
|
||||
if (m_Sections[section] != null)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (i != section && m_Sections[i] != null)
|
||||
m_Sections[i].IsHidden = true;
|
||||
}
|
||||
m_ZoomedSection = section;
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
OnZoomChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores the control so all panels are visible.
|
||||
/// </summary>
|
||||
public void UnZoom()
|
||||
{
|
||||
m_ZoomedSection = -1;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (m_Sections[i] != null)
|
||||
m_Sections[i].IsHidden = false;
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
OnZoomChanged();
|
||||
}
|
||||
}
|
||||
}
|
177
Gwen/Control/WindowControl.cs
Normal file
177
Gwen/Control/WindowControl.cs
Normal file
|
@ -0,0 +1,177 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using Gwen.ControlInternal;
|
||||
|
||||
namespace Gwen.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Movable window with title bar.
|
||||
/// </summary>
|
||||
public class WindowControl : ResizableControl
|
||||
{
|
||||
private readonly Dragger m_TitleBar;
|
||||
private readonly Label m_Caption;
|
||||
private readonly CloseButton m_CloseButton;
|
||||
private bool m_DeleteOnClose;
|
||||
private Modal m_Modal;
|
||||
|
||||
/// <summary>
|
||||
/// Window caption.
|
||||
/// </summary>
|
||||
public String Caption { get { return m_Caption.Text; } set { m_Caption.Text = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the window has close button.
|
||||
/// </summary>
|
||||
public bool IsClosable { get { return !m_CloseButton.IsHidden; } set { m_CloseButton.IsHidden = !value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the control should be disposed on close.
|
||||
/// </summary>
|
||||
public bool DeleteOnClose { get { return m_DeleteOnClose; } set { m_DeleteOnClose = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the control is hidden.
|
||||
/// </summary>
|
||||
public override bool IsHidden
|
||||
{
|
||||
get { return base.IsHidden; }
|
||||
set
|
||||
{
|
||||
if (!value)
|
||||
BringToFront();
|
||||
base.IsHidden = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WindowControl"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
/// <param name="caption">Window caption.</param>
|
||||
/// <param name="modal">Determines whether the window should be modal.</param>
|
||||
public WindowControl(Base parent, String caption = "", bool modal = false)
|
||||
: base(parent)
|
||||
{
|
||||
m_TitleBar = new Dragger(this);
|
||||
m_TitleBar.Height = 24;
|
||||
m_TitleBar.Padding = Gwen.Padding.Zero;
|
||||
m_TitleBar.Margin = new Margin(0, 0, 0, 4);
|
||||
m_TitleBar.Target = this;
|
||||
m_TitleBar.Dock = Pos.Top;
|
||||
|
||||
m_Caption = new Label(m_TitleBar);
|
||||
m_Caption.Alignment = Pos.Left | Pos.CenterV;
|
||||
m_Caption.Text = caption;
|
||||
m_Caption.Dock = Pos.Fill;
|
||||
m_Caption.Padding = new Padding(8, 0, 0, 0);
|
||||
m_Caption.TextColor = Skin.Colors.Window.TitleInactive;
|
||||
|
||||
m_CloseButton = new CloseButton(m_TitleBar, this);
|
||||
//m_CloseButton.Text = String.Empty;
|
||||
m_CloseButton.SetSize(24, 24);
|
||||
m_CloseButton.Dock = Pos.Right;
|
||||
m_CloseButton.Clicked += CloseButtonPressed;
|
||||
m_CloseButton.IsTabable = false;
|
||||
m_CloseButton.Name = "closeButton";
|
||||
|
||||
//Create a blank content control, dock it to the top - Should this be a ScrollControl?
|
||||
m_InnerPanel = new Base(this);
|
||||
m_InnerPanel.Dock = Pos.Fill;
|
||||
GetResizer(8).Hide();
|
||||
BringToFront();
|
||||
IsTabable = false;
|
||||
Focus();
|
||||
MinimumSize = new Point(100, 40);
|
||||
ClampMovement = true;
|
||||
KeyboardInputEnabled = false;
|
||||
|
||||
if (modal)
|
||||
MakeModal();
|
||||
}
|
||||
|
||||
protected virtual void CloseButtonPressed(Base control)
|
||||
{
|
||||
IsHidden = true;
|
||||
|
||||
if (m_Modal != null)
|
||||
{
|
||||
m_Modal.DelayedDelete();
|
||||
m_Modal = null;
|
||||
}
|
||||
|
||||
if (m_DeleteOnClose)
|
||||
{
|
||||
Parent.RemoveChild(this, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes the window modal: covers the whole canvas and gets all input.
|
||||
/// </summary>
|
||||
/// <param name="dim">Determines whether all the background should be dimmed.</param>
|
||||
public void MakeModal(bool dim = false)
|
||||
{
|
||||
if (m_Modal != null)
|
||||
return;
|
||||
|
||||
m_Modal = new Modal(GetCanvas());
|
||||
Parent = m_Modal;
|
||||
|
||||
if (dim)
|
||||
m_Modal.ShouldDrawBackground = true;
|
||||
else
|
||||
m_Modal.ShouldDrawBackground = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the control is on top of its parent's children.
|
||||
/// </summary>
|
||||
public override bool IsOnTop
|
||||
{
|
||||
get { return Parent.Children.Where(x => x is WindowControl).Last() == this; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
bool hasFocus = IsOnTop;
|
||||
|
||||
if (hasFocus)
|
||||
m_Caption.TextColor = Skin.Colors.Window.TitleActive;
|
||||
else
|
||||
m_Caption.TextColor = Skin.Colors.Window.TitleInactive;
|
||||
|
||||
skin.DrawWindow(this, m_TitleBar.Bottom, hasFocus);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders under the actual control (shadows etc).
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void RenderUnder(Skin.Base skin)
|
||||
{
|
||||
base.RenderUnder(skin);
|
||||
skin.DrawShadow(this);
|
||||
}
|
||||
|
||||
public override void Touch()
|
||||
{
|
||||
base.Touch();
|
||||
BringToFront();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the focus overlay.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void RenderFocus(Skin.Base skin)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
87
Gwen/ControlInternal/CategoryButton.cs
Normal file
87
Gwen/ControlInternal/CategoryButton.cs
Normal file
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Item in CollapsibleCategory.
|
||||
/// </summary>
|
||||
public class CategoryButton : Button
|
||||
{
|
||||
internal bool m_Alt; // for alternate coloring
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CategoryButton"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public CategoryButton(Base parent) : base(parent)
|
||||
{
|
||||
Alignment = Pos.Left | Pos.CenterV;
|
||||
m_Alt = false;
|
||||
IsToggle = true;
|
||||
TextPadding = new Padding(3, 0, 3, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
if (m_Alt)
|
||||
{
|
||||
if (IsDepressed || ToggleState)
|
||||
Skin.Renderer.DrawColor = skin.Colors.Category.LineAlt.Button_Selected;
|
||||
else if (IsHovered)
|
||||
Skin.Renderer.DrawColor = skin.Colors.Category.LineAlt.Button_Hover;
|
||||
else
|
||||
Skin.Renderer.DrawColor = skin.Colors.Category.LineAlt.Button;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsDepressed || ToggleState)
|
||||
Skin.Renderer.DrawColor = skin.Colors.Category.Line.Button_Selected;
|
||||
else if (IsHovered)
|
||||
Skin.Renderer.DrawColor = skin.Colors.Category.Line.Button_Hover;
|
||||
else
|
||||
Skin.Renderer.DrawColor = skin.Colors.Category.Line.Button;
|
||||
}
|
||||
|
||||
skin.Renderer.DrawFilledRect(RenderBounds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates control colors.
|
||||
/// </summary>
|
||||
public override void UpdateColors()
|
||||
{
|
||||
if (m_Alt)
|
||||
{
|
||||
if (IsDepressed || ToggleState)
|
||||
{
|
||||
TextColor = Skin.Colors.Category.LineAlt.Text_Selected;
|
||||
return;
|
||||
}
|
||||
if (IsHovered)
|
||||
{
|
||||
TextColor = Skin.Colors.Category.LineAlt.Text_Hover;
|
||||
return;
|
||||
}
|
||||
TextColor = Skin.Colors.Category.LineAlt.Text;
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsDepressed || ToggleState)
|
||||
{
|
||||
TextColor = Skin.Colors.Category.Line.Text_Selected;
|
||||
return;
|
||||
}
|
||||
if (IsHovered)
|
||||
{
|
||||
TextColor = Skin.Colors.Category.Line.Text_Hover;
|
||||
return;
|
||||
}
|
||||
TextColor = Skin.Colors.Category.Line.Text;
|
||||
}
|
||||
}
|
||||
}
|
35
Gwen/ControlInternal/CategoryHeaderButton.cs
Normal file
35
Gwen/ControlInternal/CategoryHeaderButton.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Header of CollapsibleCategory.
|
||||
/// </summary>
|
||||
public class CategoryHeaderButton : Button
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CategoryHeaderButton"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public CategoryHeaderButton(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
ShouldDrawBackground = false;
|
||||
IsToggle = true;
|
||||
Alignment = Pos.Center;
|
||||
TextPadding = new Padding(3, 0, 3, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates control colors.
|
||||
/// </summary>
|
||||
public override void UpdateColors()
|
||||
{
|
||||
if (IsDepressed || ToggleState)
|
||||
TextColor = Skin.Colors.Category.Header_Closed;
|
||||
else
|
||||
TextColor = Skin.Colors.Category.Header;
|
||||
}
|
||||
}
|
||||
}
|
33
Gwen/ControlInternal/CloseButton.cs
Normal file
33
Gwen/ControlInternal/CloseButton.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Window close button.
|
||||
/// </summary>
|
||||
public class CloseButton : Button
|
||||
{
|
||||
private readonly WindowControl m_Window;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CloseButton"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
/// <param name="owner">Window that owns this button.</param>
|
||||
public CloseButton(Base parent, WindowControl owner)
|
||||
: base(parent)
|
||||
{
|
||||
m_Window = owner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawWindowCloseButton(this, IsDepressed && IsHovered, IsHovered && ShouldDrawHover, !m_Window.IsOnTop);
|
||||
}
|
||||
}
|
||||
}
|
39
Gwen/ControlInternal/ColorButton.cs
Normal file
39
Gwen/ControlInternal/ColorButton.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Property button.
|
||||
/// </summary>
|
||||
public class ColorButton : Button
|
||||
{
|
||||
private Color m_Color;
|
||||
|
||||
/// <summary>
|
||||
/// Current color value.
|
||||
/// </summary>
|
||||
public Color Color { get { return m_Color; } set { m_Color = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ColorButton"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public ColorButton(Base parent) : base(parent)
|
||||
{
|
||||
m_Color = Color.Black;
|
||||
Text = String.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.Renderer.DrawColor = m_Color;
|
||||
skin.Renderer.DrawFilledRect(RenderBounds);
|
||||
}
|
||||
}
|
||||
}
|
45
Gwen/ControlInternal/ColorDisplay.cs
Normal file
45
Gwen/ControlInternal/ColorDisplay.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Color square.
|
||||
/// </summary>
|
||||
public class ColorDisplay : Base
|
||||
{
|
||||
private Color m_Color;
|
||||
//private bool m_DrawCheckers;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ColorDisplay"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public ColorDisplay(Base parent) : base(parent)
|
||||
{
|
||||
SetSize(32, 32);
|
||||
m_Color = Color.FromArgb(255, 255, 0, 0);
|
||||
//m_DrawCheckers = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawColorDisplay(this, m_Color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current color.
|
||||
/// </summary>
|
||||
public Color Color { get { return m_Color; } set { m_Color = value; } }
|
||||
//public bool DrawCheckers { get { return m_DrawCheckers; } set { m_DrawCheckers = value; } }
|
||||
public int R { get { return m_Color.R; } set { m_Color = Color.FromArgb(m_Color.A, value, m_Color.G, m_Color.B); } }
|
||||
public int G { get { return m_Color.G; } set { m_Color = Color.FromArgb(m_Color.A, m_Color.R, value, m_Color.B); } }
|
||||
public int B { get { return m_Color.B; } set { m_Color = Color.FromArgb(m_Color.A, m_Color.R, m_Color.G, value); } }
|
||||
public int A { get { return m_Color.A; } set { m_Color = Color.FromArgb(value, m_Color.R, m_Color.G, m_Color.B); } }
|
||||
}
|
||||
}
|
35
Gwen/ControlInternal/DownArrow.cs
Normal file
35
Gwen/ControlInternal/DownArrow.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// ComboBox arrow.
|
||||
/// </summary>
|
||||
public class DownArrow : Base
|
||||
{
|
||||
private readonly ComboBox m_ComboBox;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DownArrow"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public DownArrow(ComboBox parent)
|
||||
: base(parent) // or Base?
|
||||
{
|
||||
MouseInputEnabled = false;
|
||||
SetSize(15, 15);
|
||||
|
||||
m_ComboBox = parent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawComboBoxArrow(this, m_ComboBox.IsHovered, m_ComboBox.IsDepressed, m_ComboBox.IsOpen, m_ComboBox.IsDisabled);
|
||||
}
|
||||
}
|
||||
}
|
96
Gwen/ControlInternal/Dragger.cs
Normal file
96
Gwen/ControlInternal/Dragger.cs
Normal file
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.Control;
|
||||
using Gwen.Input;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Base for controls that can be dragged by mouse.
|
||||
/// </summary>
|
||||
public class Dragger : Base
|
||||
{
|
||||
protected bool m_Held;
|
||||
protected Point m_HoldPos;
|
||||
protected Base m_Target;
|
||||
|
||||
internal Base Target { get { return m_Target; } set { m_Target = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the control is being dragged.
|
||||
/// </summary>
|
||||
public bool IsHeld { get { return m_Held; } }
|
||||
|
||||
/// <summary>
|
||||
/// Event invoked when the control position has been changed.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Dragged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Dragger"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Dragger(Base parent) : base(parent)
|
||||
{
|
||||
MouseInputEnabled = true;
|
||||
m_Held = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="down">If set to <c>true</c> mouse button is down.</param>
|
||||
protected override void OnMouseClickedLeft(int x, int y, bool down)
|
||||
{
|
||||
if (null == m_Target) return;
|
||||
|
||||
if (down)
|
||||
{
|
||||
m_Held = true;
|
||||
m_HoldPos = m_Target.CanvasPosToLocal(new Point(x, y));
|
||||
InputHandler.MouseFocus = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Held = false;
|
||||
|
||||
InputHandler.MouseFocus = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse moved event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="dx">X change.</param>
|
||||
/// <param name="dy">Y change.</param>
|
||||
protected override void OnMouseMoved(int x, int y, int dx, int dy)
|
||||
{
|
||||
if (null == m_Target) return;
|
||||
if (!m_Held) return;
|
||||
|
||||
Point p = new Point(x - m_HoldPos.X, y - m_HoldPos.Y);
|
||||
|
||||
// Translate to parent
|
||||
if (m_Target.Parent != null)
|
||||
p = m_Target.Parent.CanvasPosToLocal(p);
|
||||
|
||||
//m_Target->SetPosition( p.x, p.y );
|
||||
m_Target.MoveTo(p.X, p.Y);
|
||||
if (Dragged != null)
|
||||
Dragged.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
29
Gwen/ControlInternal/Highlight.cs
Normal file
29
Gwen/ControlInternal/Highlight.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Drag&drop highlight.
|
||||
/// </summary>
|
||||
public class Highlight : Base
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Highlight"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Highlight(Base parent) : base(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawHighlight(this);
|
||||
}
|
||||
}
|
||||
}
|
30
Gwen/ControlInternal/MenuDivider.cs
Normal file
30
Gwen/ControlInternal/MenuDivider.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Divider menu item.
|
||||
/// </summary>
|
||||
public class MenuDivider : Base
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MenuDivider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public MenuDivider(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
Height = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawMenuDivider(this);
|
||||
}
|
||||
}
|
||||
}
|
42
Gwen/ControlInternal/Modal.cs
Normal file
42
Gwen/ControlInternal/Modal.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Modal control for windows.
|
||||
/// </summary>
|
||||
public class Modal : Base
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Modal"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Modal(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
KeyboardInputEnabled = true;
|
||||
MouseInputEnabled = true;
|
||||
ShouldDrawBackground = true;
|
||||
SetBounds(0, 0, GetCanvas().Width, GetCanvas().Height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
SetBounds(0, 0, GetCanvas().Width, GetCanvas().Height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawModalControl(this);
|
||||
}
|
||||
}
|
||||
}
|
50
Gwen/ControlInternal/PropertyRowLabel.cs
Normal file
50
Gwen/ControlInternal/PropertyRowLabel.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Label for PropertyRow.
|
||||
/// </summary>
|
||||
public class PropertyRowLabel : Label
|
||||
{
|
||||
private readonly PropertyRow m_PropertyRow;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertyRowLabel"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public PropertyRowLabel(PropertyRow parent)
|
||||
: base(parent)
|
||||
{
|
||||
Alignment = Pos.Left | Pos.CenterV;
|
||||
m_PropertyRow = parent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates control colors.
|
||||
/// </summary>
|
||||
public override void UpdateColors()
|
||||
{
|
||||
if (IsDisabled)
|
||||
{
|
||||
TextColor = Skin.Colors.Button.Disabled;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_PropertyRow != null && m_PropertyRow.IsEditing)
|
||||
{
|
||||
TextColor = Skin.Colors.Properties.Label_Selected;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_PropertyRow != null && m_PropertyRow.IsHovered)
|
||||
{
|
||||
TextColor = Skin.Colors.Properties.Label_Hover;
|
||||
return;
|
||||
}
|
||||
|
||||
TextColor = Skin.Colors.Properties.Label_Normal;
|
||||
}
|
||||
}
|
||||
}
|
30
Gwen/ControlInternal/PropertyTreeNode.cs
Normal file
30
Gwen/ControlInternal/PropertyTreeNode.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Properties node.
|
||||
/// </summary>
|
||||
public class PropertyTreeNode : TreeNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertyTreeNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public PropertyTreeNode(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_Title.TextColorOverride = Skin.Colors.Properties.Title;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawPropertyTreeNode(this, m_InnerPanel.X, m_InnerPanel.Y);
|
||||
}
|
||||
}
|
||||
}
|
154
Gwen/ControlInternal/Resizer.cs
Normal file
154
Gwen/ControlInternal/Resizer.cs
Normal file
|
@ -0,0 +1,154 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
//using System.Windows.Forms;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Grab point for resizing.
|
||||
/// </summary>
|
||||
public class Resizer : Dragger
|
||||
{
|
||||
private Pos m_ResizeDir;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the control has been resized.
|
||||
/// </summary>
|
||||
public event GwenEventHandler Resized;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Resizer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Resizer(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_ResizeDir = Pos.Left;
|
||||
MouseInputEnabled = true;
|
||||
SetSize(6, 6);
|
||||
Target = parent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse moved event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="dx">X change.</param>
|
||||
/// <param name="dy">Y change.</param>
|
||||
protected override void OnMouseMoved(int x, int y, int dx, int dy)
|
||||
{
|
||||
if (null == m_Target) return;
|
||||
if (!m_Held) return;
|
||||
|
||||
Rectangle oldBounds = m_Target.Bounds;
|
||||
Rectangle bounds = m_Target.Bounds;
|
||||
|
||||
Point min = m_Target.MinimumSize;
|
||||
|
||||
Point pCursorPos = m_Target.CanvasPosToLocal(new Point(x, y));
|
||||
|
||||
Point delta = m_Target.LocalPosToCanvas(m_HoldPos);
|
||||
delta.X -= x;
|
||||
delta.Y -= y;
|
||||
|
||||
if (0 != (m_ResizeDir & Pos.Left))
|
||||
{
|
||||
bounds.X -= delta.X;
|
||||
bounds.Width += delta.X;
|
||||
|
||||
// Conform to minimum size here so we don't
|
||||
// go all weird when we snap it in the base conrt
|
||||
|
||||
if (bounds.Width < min.X)
|
||||
{
|
||||
int diff = min.X - bounds.Width;
|
||||
bounds.Width += diff;
|
||||
bounds.X -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != (m_ResizeDir & Pos.Top))
|
||||
{
|
||||
bounds.Y -= delta.Y;
|
||||
bounds.Height += delta.Y;
|
||||
|
||||
// Conform to minimum size here so we don't
|
||||
// go all weird when we snap it in the base conrt
|
||||
|
||||
if (bounds.Height < min.Y)
|
||||
{
|
||||
int diff = min.Y - bounds.Height;
|
||||
bounds.Height += diff;
|
||||
bounds.Y -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != (m_ResizeDir & Pos.Right))
|
||||
{
|
||||
// This is complicated.
|
||||
// Basically we want to use the HoldPos, so it doesn't snap to the edge of the control
|
||||
// But we need to move the HoldPos with the window movement. Yikes.
|
||||
// I actually think this might be a big hack around the way this control works with regards
|
||||
// to the holdpos being on the parent panel.
|
||||
|
||||
int woff = bounds.Width - m_HoldPos.X;
|
||||
int diff = bounds.Width;
|
||||
bounds.Width = pCursorPos.X + woff;
|
||||
if (bounds.Width < min.X) bounds.Width = min.X;
|
||||
diff -= bounds.Width;
|
||||
|
||||
m_HoldPos.X -= diff;
|
||||
}
|
||||
|
||||
if (0 != (m_ResizeDir & Pos.Bottom))
|
||||
{
|
||||
int hoff = bounds.Height - m_HoldPos.Y;
|
||||
int diff = bounds.Height;
|
||||
bounds.Height = pCursorPos.Y + hoff;
|
||||
if (bounds.Height < min.Y) bounds.Height = min.Y;
|
||||
diff -= bounds.Height;
|
||||
|
||||
m_HoldPos.Y -= diff;
|
||||
}
|
||||
|
||||
m_Target.SetBounds(bounds);
|
||||
|
||||
if (Resized != null)
|
||||
Resized.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sizing direction.
|
||||
/// </summary>
|
||||
public Pos ResizeDir
|
||||
{
|
||||
set
|
||||
{
|
||||
m_ResizeDir = value;
|
||||
|
||||
if ((0 != (value & Pos.Left) && 0 != (value & Pos.Top)) || (0 != (value & Pos.Right) && 0 != (value & Pos.Bottom)))
|
||||
{
|
||||
//Cursor = Cursors.SizeNWSE;
|
||||
return;
|
||||
}
|
||||
if ((0 != (value & Pos.Right) && 0 != (value & Pos.Top)) || (0 != (value & Pos.Left) && 0 != (value & Pos.Bottom)))
|
||||
{
|
||||
//Cursor = Cursors.SizeNESW;
|
||||
return;
|
||||
}
|
||||
if (0 != (value & Pos.Right) || 0 != (value & Pos.Left))
|
||||
{
|
||||
//Cursor = Cursors.SizeWE;
|
||||
return;
|
||||
}
|
||||
if (0 != (value & Pos.Top) || 0 != (value & Pos.Bottom))
|
||||
{
|
||||
//Cursor = Cursors.SizeNS;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
30
Gwen/ControlInternal/RightArrow.cs
Normal file
30
Gwen/ControlInternal/RightArrow.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Submenu indicator.
|
||||
/// </summary>
|
||||
public class RightArrow : Base
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RightArrow"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public RightArrow(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
MouseInputEnabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawMenuRightArrow(this);
|
||||
}
|
||||
}
|
||||
}
|
85
Gwen/ControlInternal/ScrollBarBar.cs
Normal file
85
Gwen/ControlInternal/ScrollBarBar.cs
Normal file
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Scrollbar bar.
|
||||
/// </summary>
|
||||
public class ScrollBarBar : Dragger
|
||||
{
|
||||
private bool m_Horizontal;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the bar is horizontal.
|
||||
/// </summary>
|
||||
public bool IsHorizontal { get { return m_Horizontal; } set { m_Horizontal = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the bar is vertical.
|
||||
/// </summary>
|
||||
public bool IsVertical { get { return !m_Horizontal; } set { m_Horizontal = !value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScrollBarBar"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public ScrollBarBar(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
RestrictToParent = true;
|
||||
Target = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawScrollBarBar(this, m_Held, IsHovered, m_Horizontal);
|
||||
base.Render(skin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse moved event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="dx">X change.</param>
|
||||
/// <param name="dy">Y change.</param>
|
||||
protected override void OnMouseMoved(int x, int y, int dx, int dy)
|
||||
{
|
||||
base.OnMouseMoved(x, y, dx, dy);
|
||||
if (!m_Held)
|
||||
return;
|
||||
|
||||
InvalidateParent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked on mouse click (left) event.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="down">If set to <c>true</c> mouse button is down.</param>
|
||||
protected override void OnMouseClickedLeft(int x, int y, bool down)
|
||||
{
|
||||
base.OnMouseClickedLeft(x, y, down);
|
||||
InvalidateParent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
if (null == Parent)
|
||||
return;
|
||||
|
||||
//Move to our current position to force clamping - is this a hack?
|
||||
MoveTo(X, Y);
|
||||
}
|
||||
}
|
||||
}
|
52
Gwen/ControlInternal/ScrollBarButton.cs
Normal file
52
Gwen/ControlInternal/ScrollBarButton.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Scrollbar button.
|
||||
/// </summary>
|
||||
public class ScrollBarButton : Button
|
||||
{
|
||||
private Pos m_Direction;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScrollBarButton"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public ScrollBarButton(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetDirectionUp();
|
||||
}
|
||||
|
||||
public virtual void SetDirectionUp()
|
||||
{
|
||||
m_Direction = Pos.Top;
|
||||
}
|
||||
|
||||
public virtual void SetDirectionDown()
|
||||
{
|
||||
m_Direction = Pos.Bottom;
|
||||
}
|
||||
|
||||
public virtual void SetDirectionLeft()
|
||||
{
|
||||
m_Direction = Pos.Left;
|
||||
}
|
||||
|
||||
public virtual void SetDirectionRight()
|
||||
{
|
||||
m_Direction = Pos.Right;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawScrollButton(this, m_Direction, IsDepressed, IsHovered, IsDisabled);
|
||||
}
|
||||
}
|
||||
}
|
38
Gwen/ControlInternal/SliderBar.cs
Normal file
38
Gwen/ControlInternal/SliderBar.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Slider bar.
|
||||
/// </summary>
|
||||
public class SliderBar : Dragger
|
||||
{
|
||||
private bool m_bHorizontal;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the bar is horizontal.
|
||||
/// </summary>
|
||||
public bool IsHorizontal { get { return m_bHorizontal; } set { m_bHorizontal = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SliderBar"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public SliderBar(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
Target = this;
|
||||
RestrictToParent = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawSliderButton(this, IsHeld, IsHorizontal);
|
||||
}
|
||||
}
|
||||
}
|
41
Gwen/ControlInternal/SplitterBar.cs
Normal file
41
Gwen/ControlInternal/SplitterBar.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Splitter bar.
|
||||
/// </summary>
|
||||
public class SplitterBar : Dragger
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SplitterBar"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public SplitterBar(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
Target = this;
|
||||
RestrictToParent = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
if (ShouldDrawBackground)
|
||||
skin.DrawButton(this, true, false, IsDisabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
MoveTo(X, Y);
|
||||
}
|
||||
}
|
||||
}
|
28
Gwen/ControlInternal/TabControlInner.cs
Normal file
28
Gwen/ControlInternal/TabControlInner.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Inner panel of tab control.
|
||||
/// </summary>
|
||||
public class TabControlInner : Base
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TabControlInner"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
internal TabControlInner(Base parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawTabControl(this);
|
||||
}
|
||||
}
|
||||
}
|
210
Gwen/ControlInternal/Text.cs
Normal file
210
Gwen/ControlInternal/Text.cs
Normal file
|
@ -0,0 +1,210 @@
|
|||
//#define DEBUG_TEXT_MEASURE
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Displays text. Always sized to contents.
|
||||
/// </summary>
|
||||
public class Text : Base
|
||||
{
|
||||
private String m_String;
|
||||
private Font m_Font;
|
||||
|
||||
/// <summary>
|
||||
/// Font used to display the text.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The font is not being disposed by this class.
|
||||
/// </remarks>
|
||||
public Font Font
|
||||
{
|
||||
get { return m_Font; }
|
||||
set
|
||||
{
|
||||
m_Font = value;
|
||||
SizeToContents();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Text to display.
|
||||
/// </summary>
|
||||
public String String
|
||||
{
|
||||
get { return m_String; }
|
||||
set
|
||||
{
|
||||
m_String = value;
|
||||
SizeToContents();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Text color.
|
||||
/// </summary>
|
||||
public Color TextColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the control should be automatically resized to fit the text.
|
||||
/// </summary>
|
||||
//public bool AutoSizeToContents { get; set; } // [omeg] added
|
||||
|
||||
/// <summary>
|
||||
/// Text length in characters.
|
||||
/// </summary>
|
||||
public int Length { get { return String.Length; } }
|
||||
|
||||
/// <summary>
|
||||
/// Text color override - used by tooltips.
|
||||
/// </summary>
|
||||
public Color TextColorOverride { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Text override - used to display different string.
|
||||
/// </summary>
|
||||
public String TextOverride { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Text"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public Text(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
m_Font = Skin.DefaultFont;
|
||||
m_String = string.Empty;
|
||||
TextColor = Skin.Colors.Label.Default;
|
||||
MouseInputEnabled = false;
|
||||
TextColorOverride = Color.FromArgb(0, 255, 255, 255); // A==0, override disabled
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
if (Length == 0 || Font == null) return;
|
||||
|
||||
if (TextColorOverride.A == 0)
|
||||
skin.Renderer.DrawColor = TextColor;
|
||||
else
|
||||
skin.Renderer.DrawColor = TextColorOverride;
|
||||
|
||||
skin.Renderer.RenderText(Font, Point.Empty, TextOverride ?? String);
|
||||
|
||||
#if DEBUG_TEXT_MEASURE
|
||||
{
|
||||
Point lastPos = Point.Empty;
|
||||
|
||||
for (int i = 0; i < m_String.Length + 1; i++)
|
||||
{
|
||||
String sub = (TextOverride ?? String).Substring(0, i);
|
||||
Point p = Skin.Renderer.MeasureText(Font, sub);
|
||||
|
||||
Rectangle rect = new Rectangle();
|
||||
rect.Location = lastPos;
|
||||
rect.Size = new Size(p.X - lastPos.X, p.Y);
|
||||
skin.Renderer.DrawColor = Color.FromArgb(64, 0, 0, 0);
|
||||
skin.Renderer.DrawLinedRect(rect);
|
||||
|
||||
lastPos = new Point(rect.Right, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lays out the control's interior according to alignment, padding, dock etc.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Layout(Skin.Base skin)
|
||||
{
|
||||
SizeToContents();
|
||||
base.Layout(skin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler invoked when control's scale changes.
|
||||
/// </summary>
|
||||
protected override void OnScaleChanged()
|
||||
{
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sizes the control to its contents.
|
||||
/// </summary>
|
||||
public void SizeToContents()
|
||||
{
|
||||
if (String == null)
|
||||
return;
|
||||
|
||||
if (Font == null)
|
||||
{
|
||||
throw new InvalidOperationException("Text.SizeToContents() - No Font!!\n");
|
||||
}
|
||||
|
||||
Point p = new Point(1, Font.Size);
|
||||
|
||||
if (Length > 0)
|
||||
{
|
||||
p = Skin.Renderer.MeasureText(Font, TextOverride ?? String);
|
||||
}
|
||||
|
||||
if (p.X == Width && p.Y == Height)
|
||||
return;
|
||||
|
||||
SetSize(p.X, p.Y);
|
||||
Invalidate();
|
||||
InvalidateParent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the coordinates of specified character in the text.
|
||||
/// </summary>
|
||||
/// <param name="index">Character index.</param>
|
||||
/// <returns>Character position in local coordinates.</returns>
|
||||
public Point GetCharacterPosition(int index)
|
||||
{
|
||||
if (Length == 0 || index == 0)
|
||||
{
|
||||
return new Point(0, 0);
|
||||
}
|
||||
|
||||
String sub = (TextOverride ?? String).Substring(0, index);
|
||||
Point p = Skin.Renderer.MeasureText(Font, sub);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for a character closest to given point.
|
||||
/// </summary>
|
||||
/// <param name="p">Point.</param>
|
||||
/// <returns>Character index.</returns>
|
||||
public int GetClosestCharacter(Point p)
|
||||
{
|
||||
int distance = MaxCoord;
|
||||
int c = 0;
|
||||
|
||||
for (int i = 0; i < String.Length + 1; i++)
|
||||
{
|
||||
Point cp = GetCharacterPosition(i);
|
||||
int dist = Math.Abs(cp.X - p.X); // TODO: handle multiline
|
||||
|
||||
if (dist > distance)
|
||||
continue;
|
||||
|
||||
distance = dist;
|
||||
c = i;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
50
Gwen/ControlInternal/TreeNodeLabel.cs
Normal file
50
Gwen/ControlInternal/TreeNodeLabel.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Tree node label.
|
||||
/// </summary>
|
||||
public class TreeNodeLabel : Button
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TreeNodeLabel"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public TreeNodeLabel(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
Alignment = Pos.Left | Pos.CenterV;
|
||||
ShouldDrawBackground = false;
|
||||
Height = 16;
|
||||
TextPadding = new Padding(3, 0, 3, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates control colors.
|
||||
/// </summary>
|
||||
public override void UpdateColors()
|
||||
{
|
||||
if (IsDisabled)
|
||||
{
|
||||
TextColor = Skin.Colors.Button.Disabled;
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsDepressed || ToggleState)
|
||||
{
|
||||
TextColor = Skin.Colors.Tree.Selected;
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsHovered)
|
||||
{
|
||||
TextColor = Skin.Colors.Tree.Hover;
|
||||
return;
|
||||
}
|
||||
|
||||
TextColor = Skin.Colors.Tree.Normal;
|
||||
}
|
||||
}
|
||||
}
|
40
Gwen/ControlInternal/TreeToggleButton.cs
Normal file
40
Gwen/ControlInternal/TreeToggleButton.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Tree node toggle button (the little plus sign).
|
||||
/// </summary>
|
||||
public class TreeToggleButton : Button
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TreeToggleButton"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public TreeToggleButton(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
IsToggle = true;
|
||||
IsTabable = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the focus overlay.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void RenderFocus(Skin.Base skin)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawTreeButton(this, ToggleState);
|
||||
}
|
||||
}
|
||||
}
|
30
Gwen/ControlInternal/UpDownButton_Down.cs
Normal file
30
Gwen/ControlInternal/UpDownButton_Down.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Numeric down arrow.
|
||||
/// </summary>
|
||||
internal class UpDownButton_Down : Button
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpDownButton_Down"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public UpDownButton_Down(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetSize(7, 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawNumericUpDownButton(this, IsDepressed, false);
|
||||
}
|
||||
}
|
||||
}
|
30
Gwen/ControlInternal/UpDownButton_Up.cs
Normal file
30
Gwen/ControlInternal/UpDownButton_Up.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.ControlInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Numeric up arrow.
|
||||
/// </summary>
|
||||
public class UpDownButton_Up : Button
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpDownButton_Up"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control.</param>
|
||||
public UpDownButton_Up(Base parent)
|
||||
: base(parent)
|
||||
{
|
||||
SetSize(7, 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control using specified skin.
|
||||
/// </summary>
|
||||
/// <param name="skin">Skin to use.</param>
|
||||
protected override void Render(Skin.Base skin)
|
||||
{
|
||||
skin.DrawNumericUpDownButton(this, IsDepressed, true);
|
||||
}
|
||||
}
|
||||
}
|
244
Gwen/DragDrop/DragAndDrop.cs
Normal file
244
Gwen/DragDrop/DragAndDrop.cs
Normal file
|
@ -0,0 +1,244 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
//using System.Windows.Forms;
|
||||
using Gwen.Control;
|
||||
using Gwen.Input;
|
||||
|
||||
namespace Gwen.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Drag and drop handling.
|
||||
/// </summary>
|
||||
public static class DragAndDrop
|
||||
{
|
||||
public static Package CurrentPackage;
|
||||
public static Base HoveredControl;
|
||||
public static Base SourceControl;
|
||||
|
||||
private static Base m_LastPressedControl;
|
||||
private static Base m_NewHoveredControl;
|
||||
private static Point m_LastPressedPos;
|
||||
private static int m_MouseX;
|
||||
private static int m_MouseY;
|
||||
|
||||
private static bool onDrop(int x, int y)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
if (HoveredControl != null)
|
||||
{
|
||||
HoveredControl.DragAndDrop_HoverLeave(CurrentPackage);
|
||||
success = HoveredControl.DragAndDrop_HandleDrop(CurrentPackage, x, y);
|
||||
}
|
||||
|
||||
// Report back to the source control, to tell it if we've been successful.
|
||||
SourceControl.DragAndDrop_EndDragging(success, x, y);
|
||||
|
||||
CurrentPackage = null;
|
||||
SourceControl = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ShouldStartDraggingControl( int x, int y )
|
||||
{
|
||||
// We're not holding a control down..
|
||||
if (m_LastPressedControl == null)
|
||||
return false;
|
||||
|
||||
// Not been dragged far enough
|
||||
int length = Math.Abs(x - m_LastPressedPos.X) + Math.Abs(y - m_LastPressedPos.Y);
|
||||
if (length < 5)
|
||||
return false;
|
||||
|
||||
// Create the dragging package
|
||||
|
||||
CurrentPackage = m_LastPressedControl.DragAndDrop_GetPackage(m_LastPressedPos.X, m_LastPressedPos.Y);
|
||||
|
||||
// We didn't create a package!
|
||||
if (CurrentPackage == null)
|
||||
{
|
||||
m_LastPressedControl = null;
|
||||
SourceControl = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now we're dragging something!
|
||||
SourceControl = m_LastPressedControl;
|
||||
InputHandler.MouseFocus = null;
|
||||
m_LastPressedControl = null;
|
||||
CurrentPackage.DrawControl = null;
|
||||
|
||||
// Some controls will want to decide whether they should be dragged at that moment.
|
||||
// This function is for them (it defaults to true)
|
||||
if (!SourceControl.DragAndDrop_ShouldStartDrag())
|
||||
{
|
||||
SourceControl = null;
|
||||
CurrentPackage = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
SourceControl.DragAndDrop_StartDragging(CurrentPackage, m_LastPressedPos.X, m_LastPressedPos.Y);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void UpdateHoveredControl(Base control, int x, int y)
|
||||
{
|
||||
//
|
||||
// We use this global variable to represent our hovered control
|
||||
// That way, if the new hovered control gets deleted in one of the
|
||||
// Hover callbacks, we won't be left with a hanging pointer.
|
||||
// This isn't ideal - but it's minimal.
|
||||
//
|
||||
m_NewHoveredControl = control;
|
||||
|
||||
// Nothing to change..
|
||||
if (HoveredControl == m_NewHoveredControl)
|
||||
return;
|
||||
|
||||
// We changed - tell the old hovered control that it's no longer hovered.
|
||||
if (HoveredControl != null && HoveredControl != m_NewHoveredControl)
|
||||
HoveredControl.DragAndDrop_HoverLeave(CurrentPackage);
|
||||
|
||||
// If we're hovering where the control came from, just forget it.
|
||||
// By changing it to null here we're not going to show any error cursors
|
||||
// it will just do nothing if you drop it.
|
||||
if (m_NewHoveredControl == SourceControl)
|
||||
m_NewHoveredControl = null;
|
||||
|
||||
// Check to see if the new potential control can accept this type of package.
|
||||
// If not, ignore it and show an error cursor.
|
||||
while (m_NewHoveredControl != null && !m_NewHoveredControl.DragAndDrop_CanAcceptPackage(CurrentPackage))
|
||||
{
|
||||
// We can't drop on this control, so lets try to drop
|
||||
// onto its parent..
|
||||
m_NewHoveredControl = m_NewHoveredControl.Parent;
|
||||
|
||||
// Its parents are dead. We can't drop it here.
|
||||
// Show the NO WAY cursor.
|
||||
if (m_NewHoveredControl == null)
|
||||
{
|
||||
//Platform.Neutral.SetCursor(Cursors.No);
|
||||
}
|
||||
}
|
||||
|
||||
// Become out new hovered control
|
||||
HoveredControl = m_NewHoveredControl;
|
||||
|
||||
// If we exist, tell us that we've started hovering.
|
||||
if (HoveredControl != null)
|
||||
{
|
||||
HoveredControl.DragAndDrop_HoverEnter(CurrentPackage, x, y);
|
||||
}
|
||||
|
||||
m_NewHoveredControl = null;
|
||||
}
|
||||
|
||||
public static bool Start(Base control, Package package)
|
||||
{
|
||||
if (CurrentPackage != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CurrentPackage = package;
|
||||
SourceControl = control;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool OnMouseButton(Base hoveredControl, int x, int y, bool down)
|
||||
{
|
||||
if (!down)
|
||||
{
|
||||
m_LastPressedControl = null;
|
||||
|
||||
// Not carrying anything, allow normal actions
|
||||
if (CurrentPackage == null)
|
||||
return false;
|
||||
|
||||
// We were carrying something, drop it.
|
||||
onDrop(x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hoveredControl == null)
|
||||
return false;
|
||||
if (!hoveredControl.DragAndDrop_Draggable())
|
||||
return false;
|
||||
|
||||
// Store the last clicked on control. Don't do anything yet,
|
||||
// we'll check it in OnMouseMoved, and if it moves further than
|
||||
// x pixels with the mouse down, we'll start to drag.
|
||||
m_LastPressedPos = new Point(x, y);
|
||||
m_LastPressedControl = hoveredControl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void OnMouseMoved(Base hoveredControl, int x, int y)
|
||||
{
|
||||
// Always keep these up to date, they're used to draw the dragged control.
|
||||
m_MouseX = x;
|
||||
m_MouseY = y;
|
||||
|
||||
// If we're not carrying anything, then check to see if we should
|
||||
// pick up from a control that we're holding down. If not, then forget it.
|
||||
if (CurrentPackage == null && !ShouldStartDraggingControl(x, y))
|
||||
return;
|
||||
|
||||
// Swap to this new hovered control and notify them of the change.
|
||||
UpdateHoveredControl(hoveredControl, x, y);
|
||||
|
||||
if (HoveredControl == null)
|
||||
return;
|
||||
|
||||
// Update the hovered control every mouse move, so it can show where
|
||||
// the dropped control will land etc..
|
||||
HoveredControl.DragAndDrop_Hover(CurrentPackage, x, y);
|
||||
|
||||
// Override the cursor - since it might have been set my underlying controls
|
||||
// Ideally this would show the 'being dragged' control. TODO
|
||||
//Platform.Neutral.SetCursor(Cursors.Default);
|
||||
|
||||
hoveredControl.Redraw();
|
||||
}
|
||||
|
||||
public static void RenderOverlay(Canvas canvas, Skin.Base skin)
|
||||
{
|
||||
if (CurrentPackage == null)
|
||||
return;
|
||||
if (CurrentPackage.DrawControl == null)
|
||||
return;
|
||||
|
||||
Point old = skin.Renderer.RenderOffset;
|
||||
|
||||
skin.Renderer.AddRenderOffset(new Rectangle(
|
||||
m_MouseX - SourceControl.X - CurrentPackage.HoldOffset.X,
|
||||
m_MouseY - SourceControl.Y - CurrentPackage.HoldOffset.Y, 0, 0));
|
||||
CurrentPackage.DrawControl.DoRender(skin);
|
||||
|
||||
skin.Renderer.RenderOffset = old;
|
||||
}
|
||||
|
||||
public static void ControlDeleted(Base control)
|
||||
{
|
||||
if (SourceControl == control)
|
||||
{
|
||||
SourceControl = null;
|
||||
CurrentPackage = null;
|
||||
HoveredControl = null;
|
||||
m_LastPressedControl = null;
|
||||
}
|
||||
|
||||
if (m_LastPressedControl == control)
|
||||
m_LastPressedControl = null;
|
||||
|
||||
if (HoveredControl == control)
|
||||
HoveredControl = null;
|
||||
|
||||
if (m_NewHoveredControl == control)
|
||||
m_NewHoveredControl = null;
|
||||
}
|
||||
}
|
||||
}
|
15
Gwen/DragDrop/Package.cs
Normal file
15
Gwen/DragDrop/Package.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using Gwen.Control;
|
||||
|
||||
namespace Gwen.DragDrop
|
||||
{
|
||||
public class Package
|
||||
{
|
||||
public String Name;
|
||||
public object UserData;
|
||||
public bool IsDraggable;
|
||||
public Base DrawControl;
|
||||
public Point HoldOffset;
|
||||
}
|
||||
}
|
98
Gwen/Font.cs
Normal file
98
Gwen/Font.cs
Normal file
|
@ -0,0 +1,98 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents font resource.
|
||||
/// </summary>
|
||||
public class Font : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Font face name. Exact meaning depends on renderer.
|
||||
/// </summary>
|
||||
public String FaceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Font size.
|
||||
/// </summary>
|
||||
public int Size { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables font smoothing (default: disabled).
|
||||
/// </summary>
|
||||
public bool Smooth { get; set; }
|
||||
|
||||
//public bool Bold { get; set; }
|
||||
//public bool DropShadow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This should be set by the renderer if it tries to use a font where it's null.
|
||||
/// </summary>
|
||||
public object RendererData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is the real font size, after it's been scaled by Renderer.Scale()
|
||||
/// </summary>
|
||||
public float RealSize { get; set; }
|
||||
|
||||
private readonly Renderer.Base m_Renderer;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Font"/> class.
|
||||
/// </summary>
|
||||
public Font(Renderer.Base renderer)
|
||||
: this(renderer, "Arial", 10)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Font"/> class.
|
||||
/// </summary>
|
||||
/// <param name="renderer">Renderer to use.</param>
|
||||
/// <param name="faceName">Face name.</param>
|
||||
/// <param name="size">Font size.</param>
|
||||
public Font(Renderer.Base renderer, String faceName, int size = 10)
|
||||
{
|
||||
m_Renderer = renderer;
|
||||
FaceName = faceName;
|
||||
Size = size;
|
||||
Smooth = false;
|
||||
//Bold = false;
|
||||
//DropShadow = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
m_Renderer.FreeFont(this);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
~Font()
|
||||
{
|
||||
throw new InvalidOperationException(String.Format("IDisposable object finalized: {0}", GetType()));
|
||||
//Debug.Print(String.Format("IDisposable object finalized: {0}", GetType()));
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Duplicates font data (except renderer data which must be reinitialized).
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Font Copy()
|
||||
{
|
||||
Font f = new Font(m_Renderer, FaceName);
|
||||
f.Size = Size;
|
||||
f.RealSize = RealSize;
|
||||
f.RendererData = null; // must be reinitialized
|
||||
//f.Bold = Bold;
|
||||
//f.DropShadow = DropShadow;
|
||||
|
||||
return f;
|
||||
}
|
||||
}
|
||||
}
|
157
Gwen/Gwen.csproj
Normal file
157
Gwen/Gwen.csproj
Normal file
|
@ -0,0 +1,157 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>10.0.0</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{D3F5E624-3AF2-418F-A180-8A4172928065}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>Gwen</RootNamespace>
|
||||
<AssemblyName>Gwen</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<GenerateDocumentation>true</GenerateDocumentation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<GenerateDocumentation>true</GenerateDocumentation>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Drawing" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
<Compile Include="Align.cs" />
|
||||
<Compile Include="Font.cs" />
|
||||
<Compile Include="HSV.cs" />
|
||||
<Compile Include="Key.cs" />
|
||||
<Compile Include="Margin.cs" />
|
||||
<Compile Include="Padding.cs" />
|
||||
<Compile Include="Pos.cs" />
|
||||
<Compile Include="Texture.cs" />
|
||||
<Compile Include="ToolTip.cs" />
|
||||
<Compile Include="Util.cs" />
|
||||
<Compile Include="Anim\Animation.cs" />
|
||||
<Compile Include="Anim\TimedAnimation.cs" />
|
||||
<Compile Include="Anim\Size\Height.cs" />
|
||||
<Compile Include="Anim\Size\Width.cs" />
|
||||
<Compile Include="Control\Base.cs" />
|
||||
<Compile Include="Control\Button.cs" />
|
||||
<Compile Include="Control\Canvas.cs" />
|
||||
<Compile Include="Control\CheckBox.cs" />
|
||||
<Compile Include="Control\CollapsibleCategory.cs" />
|
||||
<Compile Include="Control\CollapsibleList.cs" />
|
||||
<Compile Include="Control\ColorLerpBox.cs" />
|
||||
<Compile Include="Control\ColorPicker.cs" />
|
||||
<Compile Include="Control\ColorSlider.cs" />
|
||||
<Compile Include="Control\ComboBox.cs" />
|
||||
<Compile Include="Control\CrossSplitter.cs" />
|
||||
<Compile Include="Control\DockBase.cs" />
|
||||
<Compile Include="Control\DockedTabControl.cs" />
|
||||
<Compile Include="Control\GroupBox.cs" />
|
||||
<Compile Include="Control\HSVColorPicker.cs" />
|
||||
<Compile Include="Control\HorizontalScrollBar.cs" />
|
||||
<Compile Include="Control\HorizontalSlider.cs" />
|
||||
<Compile Include="Control\HorizontalSplitter.cs" />
|
||||
<Compile Include="Control\IColorPicker.cs" />
|
||||
<Compile Include="Control\ImagePanel.cs" />
|
||||
<Compile Include="Control\Label.cs" />
|
||||
<Compile Include="Control\LabelClickable.cs" />
|
||||
<Compile Include="Control\LabeledCheckBox.cs" />
|
||||
<Compile Include="Control\LabeledRadioButton.cs" />
|
||||
<Compile Include="Control\ListBox.cs" />
|
||||
<Compile Include="Control\ListBoxRow.cs" />
|
||||
<Compile Include="Control\Menu.cs" />
|
||||
<Compile Include="Control\MenuItem.cs" />
|
||||
<Compile Include="Control\MenuStrip.cs" />
|
||||
<Compile Include="Control\MessageBox.cs" />
|
||||
<Compile Include="Control\NumericUpDown.cs" />
|
||||
<Compile Include="Control\ProgressBar.cs" />
|
||||
<Compile Include="Control\Properties.cs" />
|
||||
<Compile Include="Control\PropertyRow.cs" />
|
||||
<Compile Include="Control\PropertyTree.cs" />
|
||||
<Compile Include="Control\RadioButton.cs" />
|
||||
<Compile Include="Control\RadioButtonGroup.cs" />
|
||||
<Compile Include="Control\ResizableControl.cs" />
|
||||
<Compile Include="Control\RichLabel.cs" />
|
||||
<Compile Include="Control\ScrollBar.cs" />
|
||||
<Compile Include="Control\ScrollControl.cs" />
|
||||
<Compile Include="Control\Slider.cs" />
|
||||
<Compile Include="Control\StatusBar.cs" />
|
||||
<Compile Include="Control\TabButton.cs" />
|
||||
<Compile Include="Control\TabControl.cs" />
|
||||
<Compile Include="Control\TabStrip.cs" />
|
||||
<Compile Include="Control\TabTitleBar.cs" />
|
||||
<Compile Include="Control\TextBox.cs" />
|
||||
<Compile Include="Control\TextBoxNumeric.cs" />
|
||||
<Compile Include="Control\TextBoxPassword.cs" />
|
||||
<Compile Include="Control\TreeControl.cs" />
|
||||
<Compile Include="Control\TreeNode.cs" />
|
||||
<Compile Include="Control\VerticalScrollBar.cs" />
|
||||
<Compile Include="Control\VerticalSlider.cs" />
|
||||
<Compile Include="Control\VerticalSplitter.cs" />
|
||||
<Compile Include="Control\WindowControl.cs" />
|
||||
<Compile Include="Control\Layout\Positioner.cs" />
|
||||
<Compile Include="Control\Layout\Splitter.cs" />
|
||||
<Compile Include="Control\Layout\Table.cs" />
|
||||
<Compile Include="Control\Layout\TableRow.cs" />
|
||||
<Compile Include="Control\Property\Base.cs" />
|
||||
<Compile Include="Control\Property\Check.cs" />
|
||||
<Compile Include="Control\Property\Color.cs" />
|
||||
<Compile Include="Control\Property\Text.cs" />
|
||||
<Compile Include="ControlInternal\CategoryButton.cs" />
|
||||
<Compile Include="ControlInternal\CategoryHeaderButton.cs" />
|
||||
<Compile Include="ControlInternal\CloseButton.cs" />
|
||||
<Compile Include="ControlInternal\ColorButton.cs" />
|
||||
<Compile Include="ControlInternal\ColorDisplay.cs" />
|
||||
<Compile Include="ControlInternal\DownArrow.cs" />
|
||||
<Compile Include="ControlInternal\Dragger.cs" />
|
||||
<Compile Include="ControlInternal\Highlight.cs" />
|
||||
<Compile Include="ControlInternal\MenuDivider.cs" />
|
||||
<Compile Include="ControlInternal\Modal.cs" />
|
||||
<Compile Include="ControlInternal\PropertyRowLabel.cs" />
|
||||
<Compile Include="ControlInternal\PropertyTreeNode.cs" />
|
||||
<Compile Include="ControlInternal\Resizer.cs" />
|
||||
<Compile Include="ControlInternal\RightArrow.cs" />
|
||||
<Compile Include="ControlInternal\ScrollBarBar.cs" />
|
||||
<Compile Include="ControlInternal\ScrollBarButton.cs" />
|
||||
<Compile Include="ControlInternal\SliderBar.cs" />
|
||||
<Compile Include="ControlInternal\SplitterBar.cs" />
|
||||
<Compile Include="ControlInternal\TabControlInner.cs" />
|
||||
<Compile Include="ControlInternal\Text.cs" />
|
||||
<Compile Include="ControlInternal\TreeNodeLabel.cs" />
|
||||
<Compile Include="ControlInternal\TreeToggleButton.cs" />
|
||||
<Compile Include="ControlInternal\UpDownButton_Down.cs" />
|
||||
<Compile Include="ControlInternal\UpDownButton_Up.cs" />
|
||||
<Compile Include="DragDrop\DragAndDrop.cs" />
|
||||
<Compile Include="DragDrop\Package.cs" />
|
||||
<Compile Include="Input\InputHandler.cs" />
|
||||
<Compile Include="Input\KeyData.cs" />
|
||||
<Compile Include="Platform\Neutral.cs" />
|
||||
<Compile Include="Platform\Windows.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Renderer\Base.cs" />
|
||||
<Compile Include="Renderer\ICacheToTexture.cs" />
|
||||
<Compile Include="Skin\Base.cs" />
|
||||
<Compile Include="Skin\Simple.cs" />
|
||||
<Compile Include="Skin\SkinColors.cs" />
|
||||
<Compile Include="Skin\TexturedBase.cs" />
|
||||
<Compile Include="Skin\Texturing\Bordered.cs" />
|
||||
<Compile Include="Skin\Texturing\Single.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
11
Gwen/HSV.cs
Normal file
11
Gwen/HSV.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace Gwen
|
||||
{
|
||||
public struct HSV
|
||||
{
|
||||
public float h;
|
||||
public float s;
|
||||
public float v;
|
||||
}
|
||||
}
|
410
Gwen/Input/InputHandler.cs
Normal file
410
Gwen/Input/InputHandler.cs
Normal file
|
@ -0,0 +1,410 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Gwen.Control;
|
||||
using Gwen.DragDrop;
|
||||
|
||||
namespace Gwen.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Input handling.
|
||||
/// </summary>
|
||||
public static class InputHandler
|
||||
{
|
||||
private static readonly KeyData m_KeyData = new KeyData();
|
||||
private static readonly float[] m_LastClickTime = new float[MaxMouseButtons];
|
||||
private static Point m_LastClickPos;
|
||||
|
||||
/// <summary>
|
||||
/// Control currently hovered by mouse.
|
||||
/// </summary>
|
||||
public static Base HoveredControl;
|
||||
|
||||
/// <summary>
|
||||
/// Control that corrently has keyboard focus.
|
||||
/// </summary>
|
||||
public static Base KeyboardFocus;
|
||||
|
||||
/// <summary>
|
||||
/// Control that currently has mouse focus.
|
||||
/// </summary>
|
||||
public static Base MouseFocus;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of mouse buttons supported.
|
||||
/// </summary>
|
||||
public static int MaxMouseButtons { get { return 5; } }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum time in seconds between mouse clicks to be recognized as double click.
|
||||
/// </summary>
|
||||
public static float DoubleClickSpeed { get { return 0.5f; } }
|
||||
|
||||
/// <summary>
|
||||
/// Time in seconds between autorepeating of keys.
|
||||
/// </summary>
|
||||
public static float KeyRepeatRate { get { return 0.03f; } }
|
||||
|
||||
/// <summary>
|
||||
/// Time in seconds before key starts to autorepeat.
|
||||
/// </summary>
|
||||
public static float KeyRepeatDelay { get { return 0.5f; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the left mouse button is down.
|
||||
/// </summary>
|
||||
public static bool IsLeftMouseDown { get { return m_KeyData.LeftMouseDown; } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the right mouse button is down.
|
||||
/// </summary>
|
||||
public static bool IsRightMouseDown { get { return m_KeyData.RightMouseDown; } }
|
||||
|
||||
/// <summary>
|
||||
/// Current mouse position.
|
||||
/// </summary>
|
||||
public static Point MousePosition; // not property to allow modification of Point fields
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the shift key is down.
|
||||
/// </summary>
|
||||
public static bool IsShiftDown { get { return IsKeyDown(Key.Shift); } }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the control key is down.
|
||||
/// </summary>
|
||||
public static bool IsControlDown { get { return IsKeyDown(Key.Control); } }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given key is pressed.
|
||||
/// </summary>
|
||||
/// <param name="key">Key to check.</param>
|
||||
/// <returns>True if the key is down.</returns>
|
||||
public static bool IsKeyDown(Key key)
|
||||
{
|
||||
return m_KeyData.KeyState[(int)key];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles copy, paste etc.
|
||||
/// </summary>
|
||||
/// <param name="canvas">Canvas.</param>
|
||||
/// <param name="chr">Input character.</param>
|
||||
/// <returns>True if the key was handled.</returns>
|
||||
public static bool DoSpecialKeys(Base canvas, char chr)
|
||||
{
|
||||
if (null == KeyboardFocus) return false;
|
||||
if (KeyboardFocus.GetCanvas() != canvas) return false;
|
||||
if (!KeyboardFocus.IsVisible) return false;
|
||||
if (!IsControlDown) return false;
|
||||
|
||||
if (chr == 'C' || chr == 'c')
|
||||
{
|
||||
KeyboardFocus.InputCopy(null);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (chr == 'V' || chr == 'v')
|
||||
{
|
||||
KeyboardFocus.InputPaste(null);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (chr == 'X' || chr == 'x')
|
||||
{
|
||||
KeyboardFocus.InputCut(null);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (chr == 'A' || chr == 'a')
|
||||
{
|
||||
KeyboardFocus.InputSelectAll(null);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles accelerator input.
|
||||
/// </summary>
|
||||
/// <param name="canvas">Canvas.</param>
|
||||
/// <param name="chr">Input character.</param>
|
||||
/// <returns>True if the key was handled.</returns>
|
||||
public static bool HandleAccelerator(Base canvas, char chr)
|
||||
{
|
||||
//Build the accelerator search string
|
||||
StringBuilder accelString = new StringBuilder();
|
||||
if (IsControlDown)
|
||||
accelString.Append("CTRL+");
|
||||
if (IsShiftDown)
|
||||
accelString.Append("SHIFT+");
|
||||
// [omeg] todo: alt?
|
||||
|
||||
accelString.Append(chr);
|
||||
String acc = accelString.ToString();
|
||||
|
||||
//Debug::Msg("Accelerator string :%S\n", accelString.c_str());)
|
||||
|
||||
if (KeyboardFocus != null && KeyboardFocus.HandleAccelerator(acc))
|
||||
return true;
|
||||
|
||||
if (MouseFocus != null && MouseFocus.HandleAccelerator(acc))
|
||||
return true;
|
||||
|
||||
if (canvas.HandleAccelerator(acc))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mouse moved handler.
|
||||
/// </summary>
|
||||
/// <param name="canvas">Canvas.</param>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <param name="dx"></param>
|
||||
/// <param name="dy"></param>
|
||||
public static void OnMouseMoved(Base canvas, int x, int y, int dx, int dy)
|
||||
{
|
||||
// Send input to canvas for study
|
||||
MousePosition.X = x;
|
||||
MousePosition.Y = y;
|
||||
|
||||
UpdateHoveredControl(canvas);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles focus updating and key autorepeats.
|
||||
/// </summary>
|
||||
/// <param name="control">Unused.</param>
|
||||
public static void OnCanvasThink(Base control)
|
||||
{
|
||||
if (MouseFocus != null && !MouseFocus.IsVisible)
|
||||
MouseFocus = null;
|
||||
|
||||
if (KeyboardFocus != null && (!KeyboardFocus.IsVisible || !KeyboardFocus.KeyboardInputEnabled))
|
||||
KeyboardFocus = null;
|
||||
|
||||
if (null == KeyboardFocus) return;
|
||||
if (KeyboardFocus.GetCanvas() != control) return;
|
||||
|
||||
float time = Platform.Neutral.GetTimeInSeconds();
|
||||
|
||||
//
|
||||
// Simulate Key-Repeats
|
||||
//
|
||||
for (int i = 0; i < (int)Key.Count; i++)
|
||||
{
|
||||
if (m_KeyData.KeyState[i] && m_KeyData.Target != KeyboardFocus)
|
||||
{
|
||||
m_KeyData.KeyState[i] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_KeyData.KeyState[i] && time > m_KeyData.NextRepeat[i])
|
||||
{
|
||||
m_KeyData.NextRepeat[i] = Platform.Neutral.GetTimeInSeconds() + KeyRepeatRate;
|
||||
|
||||
if (KeyboardFocus != null)
|
||||
{
|
||||
KeyboardFocus.InputKeyPressed((Key)i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mouse click handler.
|
||||
/// </summary>
|
||||
/// <param name="canvas">Canvas.</param>
|
||||
/// <param name="mouseButton">Mouse button number.</param>
|
||||
/// <param name="down">Specifies if the button is down.</param>
|
||||
/// <returns>True if handled.</returns>
|
||||
public static bool OnMouseClicked(Base canvas, int mouseButton, bool down)
|
||||
{
|
||||
// If we click on a control that isn't a menu we want to close
|
||||
// all the open menus. Menus are children of the canvas.
|
||||
if (down && (null == HoveredControl || !HoveredControl.IsMenuComponent))
|
||||
{
|
||||
canvas.CloseMenus();
|
||||
}
|
||||
|
||||
if (null == HoveredControl) return false;
|
||||
if (HoveredControl.GetCanvas() != canvas) return false;
|
||||
if (!HoveredControl.IsVisible) return false;
|
||||
if (HoveredControl == canvas) return false;
|
||||
|
||||
if (mouseButton > MaxMouseButtons)
|
||||
return false;
|
||||
|
||||
if (mouseButton == 0)
|
||||
m_KeyData.LeftMouseDown = down;
|
||||
else if (mouseButton == 1)
|
||||
m_KeyData.RightMouseDown = down;
|
||||
|
||||
// Double click.
|
||||
// Todo: Shouldn't double click if mouse has moved significantly
|
||||
bool isDoubleClick = false;
|
||||
|
||||
if (down &&
|
||||
m_LastClickPos.X == MousePosition.X &&
|
||||
m_LastClickPos.Y == MousePosition.Y &&
|
||||
(Platform.Neutral.GetTimeInSeconds() - m_LastClickTime[mouseButton]) < DoubleClickSpeed)
|
||||
{
|
||||
isDoubleClick = true;
|
||||
}
|
||||
|
||||
if (down && !isDoubleClick)
|
||||
{
|
||||
m_LastClickTime[mouseButton] = Platform.Neutral.GetTimeInSeconds();
|
||||
m_LastClickPos = MousePosition;
|
||||
}
|
||||
|
||||
if (down)
|
||||
{
|
||||
FindKeyboardFocus(HoveredControl);
|
||||
}
|
||||
|
||||
HoveredControl.UpdateCursor();
|
||||
|
||||
// This tells the child it has been touched, which
|
||||
// in turn tells its parents, who tell their parents.
|
||||
// This is basically so that Windows can pop themselves
|
||||
// to the top when one of their children have been clicked.
|
||||
if (down)
|
||||
HoveredControl.Touch();
|
||||
|
||||
#if GWEN_HOOKSYSTEM
|
||||
if (bDown)
|
||||
{
|
||||
if (Hook::CallHook(&Hook::BaseHook::OnControlClicked, HoveredControl, MousePosition.x,
|
||||
MousePosition.y))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (mouseButton)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (DragAndDrop.OnMouseButton(HoveredControl, MousePosition.X, MousePosition.Y, down))
|
||||
return true;
|
||||
|
||||
if (isDoubleClick)
|
||||
HoveredControl.InputMouseDoubleClickedLeft(MousePosition.X, MousePosition.Y);
|
||||
else
|
||||
HoveredControl.InputMouseClickedLeft(MousePosition.X, MousePosition.Y, down);
|
||||
return true;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
if (isDoubleClick)
|
||||
HoveredControl.InputMouseDoubleClickedRight(MousePosition.X, MousePosition.Y);
|
||||
else
|
||||
HoveredControl.InputMouseClickedRight(MousePosition.X, MousePosition.Y, down);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Key handler.
|
||||
/// </summary>
|
||||
/// <param name="canvas">Canvas.</param>
|
||||
/// <param name="key">Key.</param>
|
||||
/// <param name="down">True if the key is down.</param>
|
||||
/// <returns>True if handled.</returns>
|
||||
public static bool OnKeyEvent(Base canvas, Key key, bool down)
|
||||
{
|
||||
if (null == KeyboardFocus) return false;
|
||||
if (KeyboardFocus.GetCanvas() != canvas) return false;
|
||||
if (!KeyboardFocus.IsVisible) return false;
|
||||
|
||||
int iKey = (int)key;
|
||||
if (down)
|
||||
{
|
||||
if (!m_KeyData.KeyState[iKey])
|
||||
{
|
||||
m_KeyData.KeyState[iKey] = true;
|
||||
m_KeyData.NextRepeat[iKey] = Platform.Neutral.GetTimeInSeconds() + KeyRepeatDelay;
|
||||
m_KeyData.Target = KeyboardFocus;
|
||||
|
||||
return KeyboardFocus.InputKeyPressed(key);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_KeyData.KeyState[iKey])
|
||||
{
|
||||
m_KeyData.KeyState[iKey] = false;
|
||||
|
||||
// BUG BUG. This causes shift left arrow in textboxes
|
||||
// to not work. What is disabling it here breaking?
|
||||
//m_KeyData.Target = NULL;
|
||||
|
||||
return KeyboardFocus.InputKeyPressed(key, false);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void UpdateHoveredControl(Base inCanvas)
|
||||
{
|
||||
Base hovered = inCanvas.GetControlAt(MousePosition.X, MousePosition.Y);
|
||||
|
||||
if (hovered != HoveredControl)
|
||||
{
|
||||
if (HoveredControl != null)
|
||||
{
|
||||
var oldHover = HoveredControl;
|
||||
HoveredControl = null;
|
||||
oldHover.InputMouseLeft();
|
||||
}
|
||||
|
||||
HoveredControl = hovered;
|
||||
|
||||
if (HoveredControl != null)
|
||||
{
|
||||
HoveredControl.InputMouseEntered();
|
||||
}
|
||||
}
|
||||
|
||||
if (MouseFocus != null && MouseFocus.GetCanvas() == inCanvas)
|
||||
{
|
||||
if (HoveredControl != null)
|
||||
{
|
||||
var oldHover = HoveredControl;
|
||||
HoveredControl = null;
|
||||
oldHover.Redraw();
|
||||
}
|
||||
HoveredControl = MouseFocus;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FindKeyboardFocus(Base control)
|
||||
{
|
||||
if (null == control) return;
|
||||
if (control.KeyboardInputEnabled)
|
||||
{
|
||||
//Make sure none of our children have keyboard focus first - todo recursive
|
||||
if (control.Children.Any(child => child == KeyboardFocus))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
control.Focus();
|
||||
return;
|
||||
}
|
||||
|
||||
FindKeyboardFocus(control.Parent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue