Torque3D/Engine/source/gui/editor/guiEditCtrl.h
2012-09-19 11:15:01 -04:00

340 lines
15 KiB
C++

//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUIEDITCTRL_H_
#define _GUIEDITCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _UNDO_H_
#include "util/undo.h"
#endif
#ifndef _GFX_GFXDRAWER_H_
#include "gfx/gfxDrawUtil.h"
#endif
/// Native side of the GUI editor.
class GuiEditCtrl : public GuiControl
{
public:
typedef GuiControl Parent;
friend class GuiEditorRuler;
enum Justification
{
JUSTIFY_LEFT,
JUSTIFY_CENTER_VERTICAL,
JUSTIFY_RIGHT,
JUSTIFY_TOP,
JUSTIFY_BOTTOM,
SPACING_VERTICAL,
SPACING_HORIZONTAL,
JUSTIFY_CENTER_HORIZONTAL,
};
enum guideAxis { GuideVertical, GuideHorizontal };
enum mouseModes { Selecting, MovingSelection, SizingSelection, DragSelecting, DragGuide, DragClone };
enum sizingModes { sizingNone = 0, sizingLeft = 1, sizingRight = 2, sizingTop = 4, sizingBottom = 8 };
enum snappingAxis { SnapVertical, SnapHorizontal };
enum snappingEdges { SnapEdgeMin, SnapEdgeMid, SnapEdgeMax };
protected:
enum
{
NUT_SIZE = 4,
MIN_GRID_SIZE = 3
};
typedef Vector< GuiControl* > GuiControlVector;
typedef SimObjectPtr< GuiControl > GuiControlPtr;
bool mDrawBorderLines;
bool mDrawGuides;
bool mFullBoxSelection;
GuiControlVector mSelectedControls;
GuiControlPtr mCurrentAddSet;
GuiControlPtr mContentControl;
Point2I mLastMousePos;
Point2I mLastDragPos;
Point2I mSelectionAnchor;
Point2I mGridSnap;
Point2I mDragBeginPoint;
Vector<Point2I> mDragBeginPoints;
bool mDragAddSelection;
bool mDragMoveUndo;
// Guides.
bool mSnapToGuides; ///< If true, edge and center snapping will work against guides.
bool mDragGuide[ 2 ];
U32 mDragGuideIndex[ 2 ];///< Indices of the guide's being dragged in DragGuide mouse mode.
Vector< U32 > mGuides[ 2 ]; ///< The guides defined on the current content control.
// Snapping
bool mSnapToControls; ///< If true, edge and center snapping will work against controls.
bool mSnapToCanvas; ///< If true, edge and center snapping will work against canvas (= content control).
bool mSnapToEdges; ///< If true, selection edges will snap to other control edges.
bool mSnapToCenters; ///< If true, selection centers will snap to other control centers.
S32 mSnapSensitivity; ///< Snap distance in pixels.
bool mSnapped[ 2 ]; ///< Snap flag for each axis. If true, we are currently snapped in a drag.
S32 mSnapOffset[ 2 ]; ///< Reference point on snap line for each axis.
GuiControlVector mSnapHits[ 2 ]; ///< Hit testing lists for each axis.
snappingEdges mSnapEdge[ 2 ]; ///< Identification of edge being snapped for each axis.
GuiControlPtr mSnapTargets[ 2 ]; ///< Controls providing the snap reference lines on each axis.
// Undo
SimGroup* mTrash;
SimSet* mSelectedSet;
// grid drawing
GFXVertexBufferHandle<GFXVertexPC> mDots;
GFXStateBlockRef mDotSB;
mouseModes mMouseDownMode;
sizingModes mSizingMode;
static StringTableEntry smGuidesPropertyName[ 2 ];
// private methods
void updateSelectedSet();
S32 findGuide( guideAxis axis, const Point2I& point, U32 tolerance = 0 );
RectI getSnapRegion( snappingAxis axis, const Point2I& center ) const;
void findSnaps( snappingAxis axis, const Point2I& mousePoint, const RectI& minRegion, const RectI& midRegion, const RectI& maxRegion );
void registerSnap( snappingAxis axis, const Point2I& mousePoint, const Point2I& point, snappingEdges edge, GuiControl* ctrl = NULL );
void doSnapping( const GuiEvent& event, const RectI& selectionBounds, Point2I& delta );
void doGuideSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta );
void doControlSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta );
void doGridSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta );
void snapToGrid( Point2I& point );
void startDragMove( const Point2I& startPoint );
void startDragRectangle( const Point2I& startPoint );
void startDragClone( const Point2I& startPoint );
void startMouseGuideDrag( guideAxis axis, U32 index, bool lockMouse = true );
void setMouseMode( mouseModes mode );
/// @name Callbacks
/// @{
DECLARE_CALLBACK( void, onHierarchyChanged, () );
DECLARE_CALLBACK( void, onDelete, () );
DECLARE_CALLBACK( void, onPreEdit, ( SimSet* selection ) );
DECLARE_CALLBACK( void, onPostEdit, ( SimSet* selection ) );
DECLARE_CALLBACK( void, onClearSelected, () );
DECLARE_CALLBACK( void, onSelect, ( GuiControl* control ) );
DECLARE_CALLBACK( void, onAddSelected, ( GuiControl* control ) );
DECLARE_CALLBACK( void, onRemoveSelected, ( GuiControl* control ) );
DECLARE_CALLBACK( void, onPreSelectionNudged, ( SimSet* selection ) );
DECLARE_CALLBACK( void, onPostSelectionNudged, ( SimSet* selection ) );
DECLARE_CALLBACK( void, onSelectionMoved, ( GuiControl* control ) ); //FIXME: this should be a selection SimSet
DECLARE_CALLBACK( void, onSelectionCloned, ( SimSet* selection ) );
DECLARE_CALLBACK( void, onTrashSelection, ( SimSet* selection ) );
DECLARE_CALLBACK( void, onAddNewCtrl, ( GuiControl* control ) );
DECLARE_CALLBACK( void, onAddNewCtrlSet, ( SimSet* set ) );
DECLARE_CALLBACK( void, onSelectionResized, ( GuiControl* control ) );
DECLARE_CALLBACK( void, onFitIntoParent, ( bool width, bool height ) );
DECLARE_CALLBACK( void, onMouseModeChange, () );
DECLARE_CALLBACK( void, onControlInspectPreApply, ( GuiControl* control ) );
DECLARE_CALLBACK( void, onControlInspectPostApply, ( GuiControl* control ) );
/// @}
static bool inNut( const Point2I &pt, S32 x, S32 y )
{
S32 dx = pt.x - x;
S32 dy = pt.y - y;
return dx <= NUT_SIZE && dx >= -NUT_SIZE && dy <= NUT_SIZE && dy >= -NUT_SIZE;
}
static void drawCrossSection( U32 axis, S32 offset, const RectI& bounds, ColorI color, GFXDrawUtil* drawer )
{
Point2I start;
Point2I end;
if( axis == 0 )
{
start.x = end.x = offset;
start.y = bounds.point.y;
end.y = bounds.point.y + bounds.extent.y - 1;
}
else
{
start.y = end.y = offset;
start.x = bounds.point.x;
end.x = bounds.point.x + bounds.extent.x - 1;
}
drawer->drawLine( start, end, color );
}
static void snapDelta( snappingAxis axis, snappingEdges edge, S32 offset, const RectI& bounds, Point2I& delta )
{
switch( axis )
{
case SnapVertical:
switch( edge )
{
case SnapEdgeMin:
delta.x = offset - bounds.point.x;
break;
case SnapEdgeMid:
delta.x = offset - bounds.point.x - bounds.extent.x / 2;
break;
case SnapEdgeMax:
delta.x = offset - bounds.point.x - bounds.extent.x + 1;
break;
}
break;
case SnapHorizontal:
switch( edge )
{
case SnapEdgeMin:
delta.y = offset - bounds.point.y;
break;
case SnapEdgeMid:
delta.y = offset - bounds.point.y - bounds.extent.y / 2;
break;
case SnapEdgeMax:
delta.y = offset - bounds.point.y - bounds.extent.y + 1;
break;
}
break;
}
}
public:
GuiEditCtrl();
DECLARE_CONOBJECT(GuiEditCtrl);
DECLARE_CATEGORY( "Gui Editor" );
DECLARE_DESCRIPTION( "Implements the framework for the GUI editor." );
bool onWake();
void onSleep();
static void initPersistFields();
GuiControl* getContentControl() const { return mContentControl; }
void setContentControl(GuiControl *ctrl);
void setEditMode(bool value);
S32 getSizingHitKnobs(const Point2I &pt, const RectI &box);
void getDragRect(RectI &b);
void drawNut(const Point2I &nut, ColorI &outlineColor, ColorI &nutColor);
void drawNuts(RectI &box, ColorI &outlineColor, ColorI &nutColor);
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
void addNewControl(GuiControl *ctrl);
void setCurrentAddSet(GuiControl *ctrl, bool clearSelection = true);
GuiControl* getCurrentAddSet();
// Selections.
void select( GuiControl *ctrl );
bool selectionContains( GuiControl *ctrl );
bool selectionContainsParentOf( GuiControl* ctrl );
void setSelection( GuiControl *ctrl, bool inclusive = false );
RectI getSelectionBounds() const;
RectI getSelectionGlobalBounds() const;
void canHitSelectedControls( bool state = true );
void justifySelection( Justification j );
void moveSelection( const Point2I &delta, bool callback = true );
void moveAndSnapSelection( const Point2I &delta, bool callback = true );
void saveSelection( const char *filename );
void loadSelection( const char *filename );
void addSelection( S32 id );
void addSelection( GuiControl* ctrl );
void removeSelection( S32 id );
void removeSelection( GuiControl* ctrl );
void deleteSelection();
void clearSelection();
void selectAll();
void bringToFront();
void pushToBack();
void moveSelectionToCtrl( GuiControl* ctrl, bool callback = true );
void addSelectControlsInRegion( const RectI& rect, U32 hitTestFlags = 0 );
void addSelectControlAt( const Point2I& pos );
void resizeControlsInSelectionBy( const Point2I& delta, U32 mode );
void fitIntoParents( bool width = true, bool height = true );
void selectParents( bool addToSelection = false );
void selectChildren( bool addToSelection = false );
void cloneSelection();
U32 getNumSelected() const { return mSelectedControls.size(); }
// Guides.
U32 addGuide( guideAxis axis, U32 offset ) { U32 index = mGuides[ axis ].size(); mGuides[ axis ].push_back( offset ); return index; }
void readGuides( guideAxis axis, GuiControl* ctrl );
void writeGuides( guideAxis axis, GuiControl* ctrl );
void clearGuides( guideAxis axis ) { mGuides[ axis ].clear(); }
// Undo Access.
void undo();
void redo();
// When a control is changed by the inspector
void controlInspectPreApply(GuiControl* object);
void controlInspectPostApply(GuiControl* object);
// Sizing Cursors
void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent);
U32 getSelectionSize() const { return mSelectedControls.size(); }
const Vector<GuiControl *>& getSelected() const { return mSelectedControls; }
SimSet* getSelectedSet() { updateSelectedSet(); return mSelectedSet; }
SimGroup* getTrash() { return mTrash; }
GuiControl* getAddSet() const { return mCurrentAddSet; }; //JDD
bool onKeyDown(const GuiEvent &event);
void onMouseDown(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
void onRightMouseDown(const GuiEvent &event);
mouseModes getMouseMode() const { return mMouseDownMode; }
virtual bool onAdd();
virtual void onRemove();
void setSnapToGrid(U32 gridsize);
};
#endif //_GUI_EDIT_CTRL_H