Newer
Older
Import / research / ui / toolkit / include / Widget.h
#pragma once
#include <list>  // using list for fast deletions and order preservation
#include "Graphics.h"
#include "Property.h"
#include "Events.h"
#include "XmlParser.h"
#include "Common.h"
#include "Object.h"
BEGIN_NAMESPACE


enum WidgetFlags
{
	WF_None = 0,
	WF_EraseBackground = 1 << 0
};
ENUM_AS_FLAGS(WidgetFlags)


enum MouseState 
{
	MS_Up,
	MS_Down,
	MS_DoubleClick
};


enum LayoutPolicy
{
  LP_Horizontal = 0,
  LP_Vertical = 1,
  LP_Unmanaged = 2
};


class PaintTargetInterface : public InterfaceBase
{
public:
	virtual PixelBuffer& targetBuffer() = 0;
	virtual float targetScale() = 0;
};


/*
enum SizeFlags
{
		SF_None,
		SF_CanGrow,
		SF_CanShrink,
		SF_
		SF_Greedy,
};


enum SizePolicy
{
	//SP_Fixed,        // Only the returned fixed size value is to be used
	SP_Minimum,		 // It can be larger, but it does not require to be larger
	SP_Preferred,	 // The returned value is best but smaller or larger is fine
	SP_Maximum,		 // It can safely be smaller but can not be larger than the provided value
	SP_Expanding
};
*/

struct SizeOptions
{
	// For a fixed size, set min, preferred and max values to the same values
	// For expanding, set min and preferred to the min value and max to a very large number
	// For shrinking, set max and preferred to the max value and min to 0
	Size m_minimum;		// value that size will not be smaller than
	Size m_preferred;	// ideal size
	Size m_maximum;		// value that size will not be larger than
	//int m_policyX, m_policyY;            // hint for 

	// Algorithm
	//   For dimension being shared across widgets
	//		First try to use the preferred values summed together and compare against space
	//		It will be equal, under or over. For equal, accept the preferred values and done.
	//		If over or under, divide this by the number of widgets and try to distriubte
	//		this while still keeping within the constraints. Keep track of how many widgets
	//		that this is not in their constraints and by how much and sum this up. Now
	//		work out the count of widgets we can shrink/grow within their constraints and
	//		divide out the amount we can distribute to them. If we get to the point that
	//		we can't meet all the constraints, we will need to either force the parent
	//		to change it's size to be larger (some how need to make sure that doesn't happen)
	//		or add padding evenly to the widgets to space them out equally
};


class Widget : public Inherit<Object,Widget>, public PaintTargetInterface  // FIXME: there is multiple inheritance happening here
{
public:
	Widget(Object* a_parent, const char* a_name);
	Widget(Widget* a_parent = 0, const char* a_name = 0,
          LayoutPolicy a_layoutDirection = LP_Horizontal, int a_x = 0, int a_y = 0, int a_width = 10, int a_height = 10);
	virtual ~Widget();
  void delayDelete();

	virtual void repaint();
	virtual void update(Rectangle& a_rectangle);

	int width();
	int height();

	virtual Rectangle worldGeometry()
  {
    Rectangle rect;
    Widget* widgetParent = parent<Widget>();
    if (widgetParent)
      rect = widgetParent->worldGeometry();
    rect.m_x += m_x;
    rect.m_y += m_y;
    return rect;
  }

	virtual void setWorldGeometry(Rectangle a_rect)
  {
    Widget* widgetParent = parent<Widget>();
    if (widgetParent)
      widgetParent->setWorldGeometry(a_rect);
  }
 
  // To send up to the window
  // TODO: needs better name
	virtual void setNewSize(int a_width, int a_height)
  {
    Widget* widgetParent = parent<Widget>();
    if (widgetParent)
      widgetParent->setNewSize(a_width, a_height);
    else
      updateLayout();
  }

	void setGeometry(int a_x, int a_y, int a_width, int a_height);
	Rectangle rectangle() // could rename to geometry,  perhaps could make it a property
	{
		Rectangle r = { {{0, 0}}, {{width(), height()}} }; return r;
	}

  // private?
	void eraseBackground(int i = 0);

  // virtual so can send up to window, but doesn't seem quite correct
	virtual TimerId startTimer(int a_milliSeconds);
	virtual void killTimer(TimerId a_timerId);

  // Allow a OpenGL widget to provide its own openGL commands
	virtual void glPaint();

  // protected / private?
	PixelBuffer& targetBuffer() override
	{
		return m_target;
	}

	float targetScale() override
  {
    return m_combinedScale;
  }

	// We expose this here publically, and as a concrete Property implementaion
	// as it is a simple type and it is concrete to the widget, eg the variable
	// belongs to widgets, so it makes sense in this case that it is a Property.
	Property<WidgetFlags>   flags;
	Property<uint32_t>      backgroundBrush;
	Property<int>           layoutSpacing;
	Property<int>           layoutMargin;
	Property<float>         scale;  // Applies a scale factor to this widget and all its decendants

  START_MEMBERS
    REFLECT_MEMBER(flags,           "widget flags")
    REFLECT_MEMBER(backgroundBrush, "background brush is style and color that the widget is cleared with")
    REFLECT_MEMBER(layoutSpacing,   "spacing between child widgets")
    REFLECT_MEMBER(layoutMargin,    "spacing around the border")
	  REFLECT_MEMBER(scale,           "scale factor")
  END_MEMBERS

protected:
	Widget(const char* a_name, bool a_root); // Constructor for root widgets - eg: a window

  // Probably all these event functions should be protected
	virtual void sizeOptions(SizeOptions& a_sizeOptions);

	virtual void sizeEvent(SizeEvent& a_event);
	virtual void paintEvent(PaintEvent& a_event);
	virtual void timerEvent(TimerEvent& a_event);
	virtual void keyEvent(KeyEvent& a_event);
	virtual void mouseEvent(MouseEvent& a_event);
	virtual void mouseEnterEvent(MouseEvent& a_event);
	virtual void mouseLeaveEvent(MouseEvent& a_event);
	virtual void wheelEvent(WheelEvent& a_event);

	virtual void event(Event& a_event);

	void updateLayout();
private:
  void collectDelayDeleteWidgets(std::vector<Widget*>& a_widgetsToBeDeleted) const;
  void deleteDelayDeleteWidgets();
	void scaledSizeOptions(SizeOptions& a_sizeOptions);
  void updateScale(float ignored);
	void updateTarget();
  void childAdded(Object* a_object) override;

	PixelBuffer m_target;
	int         m_x, m_y; // offsets from parent
	int         m_windowX, m_windowY; // offsets from window
	int         m_goalWidth, m_goalHeight; // these could be clamped to lower values if out of the window
							// TODO: probably not handling the case of geometry being set going off the left
							// or the top of the window (with child partially inside the window)
							// Off to the right or going off the bottom is handled by the clamping
	bool				m_watchingMouse;
	bool				m_mouseEntered;
	bool				m_delayDelete;
  float       m_combinedScale; // the scale relative to the parent's m_combinedScale

	LayoutPolicy m_layoutDirection;

  void        setParentHasUnmanagedChild();
  bool        m_hasUnmanagedDescendant;
};


END_NAMESPACE