#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