ultimatepp/bazaar/CtrlPos/CtrlPos.cpp
kohait 28982960b5 fixing some of my Bazaar things, work in progress
git-svn-id: svn://ultimatepp.org/upp/trunk@14139 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2020-03-08 20:37:30 +00:00

538 lines
14 KiB
C++

#include "CtrlPos.h"
void CtrlPos::DrawAlignHandle(Draw& w, const Rect& _r, const Rect& r, const Ctrl::LogPos& pos, const Color& col)
{
Point p;
bool q;
Size rz = r.GetSize();
Color col2 = Blend(col, Black);
p = r.CenterLeft();
p.y -= rz.cy/4;
q = (pos.x.GetAlign()&Ctrl::LEFT);
w.DrawLine(p, Point(_r.left, p.y), (q)?(PEN_SOLID):(PEN_DOT), (q)?(col):(col2));
p = r.CenterRight();
p.y += rz.cy/4;
q = (pos.x.GetAlign()&Ctrl::RIGHT);
w.DrawLine(p, Point(_r.right, p.y), (q)?(PEN_SOLID):(PEN_DOT), (q)?(col):(col2));
p = r.TopCenter();
p.x -= rz.cx/4;
q = (pos.y.GetAlign()&Ctrl::TOP);
w.DrawLine(p, Point(p.x, _r.top), (q)?(PEN_SOLID):(PEN_DOT), (q)?(col):(col2));
p = r.BottomCenter();
p.x += rz.cx/4;
q = (pos.y.GetAlign()&Ctrl::BOTTOM);
w.DrawLine(p, Point(p.x, _r.bottom), (q)?(PEN_SOLID):(PEN_DOT), (q)?(col):(col2));
}
bool CtrlPos::GetAlignMode(const Rect& _r, const Rect& r, const Point& pp, Ctrl::LogPos& pos, int handsize)
{
int size = handsize+2;
Rect c;
Point p;
int q;
Size rz = r.GetSize();
q = pos.x.GetAlign();
p = r.CenterLeft();
p.y -= rz.cy/4;
c = Rect(p, Point(_r.left, p.y)); c.InflateVert(size); c.Normalize();
if(c.Contains(pp)) { q&= ~Ctrl::LEFT; q|= (pos.x.GetAlign()&Ctrl::LEFT)?(0):(Ctrl::LEFT); pos.x.SetAlign(q); return true; }
p = r.CenterRight();
p.y += rz.cy/4;
c = Rect(p, Point(_r.right, p.y)); c.InflateVert(size); c.Normalize();
if(c.Contains(pp)) { q&= ~Ctrl::RIGHT; q|= (pos.x.GetAlign()&Ctrl::RIGHT)?(0):(Ctrl::RIGHT); pos.x.SetAlign(q); return true; }
q = pos.y.GetAlign();
p = r.TopCenter();
p.x -= rz.cx/4;
c = Rect(p, Point(p.x, _r.top)); c.InflateHorz(size); c.Normalize();
if(c.Contains(pp)) { q&= ~Ctrl::TOP; q|= (pos.y.GetAlign()&Ctrl::TOP)?(0):(Ctrl::TOP); pos.y.SetAlign(q); return true; }
p = r.BottomCenter();
p.x += rz.cx/4;
c = Rect(p, Point(p.x, _r.bottom)); c.InflateHorz(size); c.Normalize();
if(c.Contains(pp)) { q&= ~Ctrl::BOTTOM; q|= (pos.y.GetAlign()&Ctrl::BOTTOM)?(0):(Ctrl::BOTTOM); pos.y.SetAlign(q); return true; }
return false;
}
void CtrlPos::GetAlignRects(const Ctrl& ctxuser, const Ctrl& finalctx, Rect& r, Rect& _r)
{
r = ctxuser.GetRect();
Point op = GetOffset(ctxuser, finalctx);
r.Offset(op);
if(ctxuser.InView())
{
_r = ctxuser.GetParent()->GetView();
_r.Offset(op - _r.TopLeft()); //revert own offset which has been added by GetOffset
}
else //in frame, support makes no sense, actually, frames shouldnt be moveable
{
_r = ctxuser.GetParent()->GetRect();
Point _op = GetOffset(*ctxuser.GetParent(), finalctx); //without view, so real parents's offset
_r.Offset(_op);
}
}
void CtrlPos::DrawHintFrame(Draw& w, const Ctrl& g, const Ctrl& q, const Color& hintcol, const CtrlFilterType& filter, int flags)
{
Ctrl* c = g.GetFirstChild();
while(c)
{
Ctrl* e(c);
Point p; p.Clear();
int f(flags);
filter(e, f);
if(e && c->InView())
{
Rect r = c->GetRect();
r.Offset(GetOffset(*c, q));
r.Inflate(1);
RectCtrl::DrawHandleFrame(w, r, hintcol, 1);
}
if(f & CtrlFinder::DEEP)
DrawHintFrame(w, *c, q, hintcol, filter, f);
c = c->GetNext();
}
}
//remove each element of _c in c, if found
void CtrlPos::CombineSubtract(Vector<Ctrl*>& c, Vector<Ctrl*>&& _c)
{
//top is the currently editable ctrl
Vector<Ctrl*> __c = pick(_c);
while(__c.GetCount() > 0)
{
int i = FindIndex(c, __c.Top()); //with this we are O(N^2)
if(i>=0) c.Remove(i);
__c.Drop();
}
}
void CtrlPos::CombineAdd(Vector<Ctrl*>& c, Vector<Ctrl*>&& _c)
{
//top is the currently editable ctrl
//first, we remove every duplicate, before we take everything else
CombineSubtract(c, Vector<Ctrl*>(_c, 1)); //deep copy to keep our copy
c.Append(_c);
}
void CtrlPos::DrawSelected(Draw& w, const Vector<Ctrl*>& ctrls)
{
for(int i = 0; i < ctrls.GetCount(); i++)
{
Ctrl& c = *ctrls[i];
if(&c == pctrl) return; //this will be the only one in the vector
Rect r; //the rect of the ctrl
Rect _r; //the outer parent ctrl rect
GetAlignRects(c, *pctrl, r, _r);
if(&c == ctrls.Top())
{
//top, the one that all controling info comes from or goes to in a multi select context
DrawAlignHandle(w, _r, r, c.GetPos(), style->handcol);
RectCtrl::DrawHandleFrame(w, r, style->handcol, style->framesize);
RectCtrl::DrawHandle(w, r, style->handcol, style->handsize);
RectCtrl::DrawHandleFrame(w, _r, LtRed(), style->framesize); //debug only maybe?
}
else
{
RectCtrl::DrawHandleFrame(w, r, style->framecol, style->framesize);
}
}
}
void CtrlPos::Paint(Draw& w)
{
Size sz = GetSize();
if(!IsTransparent())
w.DrawRect(0,0,sz.cx,sz.cy, SColorFace());
if(!IsEnabled()) return;
if(pctrl)
DrawHintFrame(w, *pctrl, *pctrl, LtGray(), filter, flags);
if(ctrls.IsEmpty()) return;
DrawSelected(w, ctrls);
if(pressed)// && moving)
RectCtrl::DrawRectInfo(w, Point(10,10), ctrls.Top()->GetRect(), style->framecol, style->textcol);
//RectCtrl::DrawHandleFrame(w, rd, LtGreen(), style->framesize); //debug
//RectCtrl::DrawHandleFrame(w, rd2, Yellow(), style->framesize); //debug
}
void CtrlPos::LeftDown(Point p, dword keyflags)
{
if(IsReadOnly() || !IsEnabled()) return;
SetCapture();
moving = false;
pressed = (keyflags & K_MOUSELEFT);
if(!ctrls.IsEmpty())
{
//if already found prepare moving or dragging handles
//will be dealing with .Top() as the handled Ctrl
Ctrl& c = *ctrls.Top();
//save old data
xp = p;
xpos.SetCount(ctrls.GetCount());
xpars.SetCount(ctrls.GetCount());
xop.SetCount(ctrls.GetCount());
xr.SetCount(ctrls.GetCount());
for(int i = 0; i < ctrls.GetCount(); i++)
{
Ctrl& c = *ctrls[i];
ASSERT(!c.InFrame());
xpos[i] = c.GetPos();
xpars[i] = c.GetParent();
xop[i] = GetOffset(c, *pctrl);
xr[i] = c.GetRect();
}
Rect r; //the rect of the ctrl
Rect _r; //the outer parent ctrl rect
GetAlignRects(c, *pctrl, r, _r);
Ctrl::LogPos pos = xpos.Top();
Rect rr(r); rr.Inflate(style->handsize+2);
//process alignment or prepare cursor for handles
if(GetAlignMode(_r, rr, p, pos, style->handsize))
{
//derive new alignment from top to others
for(int i = 0; i < ctrls.GetCount(); i++)
{
Ctrl& c = *ctrls[i];
Ctrl::LogPos _pos = LogPosPopUp::MakeLogPos(pos, c);
c.SetPos(_pos);
}
Action();
Refresh();
return;
}
else if((mode = RectCtrl::GetMode(r, p, keyflags, style->handsize)) != RectCtrl::NONE)
{
ci = RectCtrl::SetCursor(mode, keyflags, ci);
//no action, nothing happened yet
Refresh();
return;
}
//else fallback to finding a ctrl
}
if(!pctrl) return;
//start finding
Point pt(p);
int f(flags);
Rect r; r.Clear();
//we can try to find multiple first
if(multi)
{
RectTracker tr(*this);
r = tr.Track(Rect(p,p), ALIGN_NULL, ALIGN_NULL);
}
if(!r.IsEmpty())
{
//try find multiple
Vector<Ctrl*> _ctrls;
_ctrls = GetCtrls(*pctrl, r, f, filter);
if(_ctrls.IsEmpty() || !_ctrls.Top())
ctrls.Clear();
else if(keyflags & K_CTRL)
{
//add or remove probable found additional controls
if(keyflags & K_SHIFT)
CombineSubtract(ctrls, pick(_ctrls));
else
CombineAdd(ctrls, pick(_ctrls));
}
else
ctrls = pick(_ctrls);
if(!ctrls.IsEmpty())
WhenLeftSelectMulti(&ctrls, r, keyflags);
}
else
{
//try find single
Vector<Ctrl*> _ctrls;
_ctrls << GetCtrl(*pctrl, pt, f, filter);
if(_ctrls.IsEmpty() || !_ctrls.Top()
|| _ctrls.Top()->InFrame() //dont allow moving frame around
)
ctrls.Clear();
else if(keyflags & K_CTRL)
{
//add or remove probable found additional controls
if(keyflags & K_SHIFT)
CombineSubtract(ctrls, pick(_ctrls));
else
CombineAdd(ctrls, pick(_ctrls));
}
else
ctrls = pick(_ctrls);
if(!ctrls.IsEmpty())
WhenLeftSelect(*ctrls.Top(), p, keyflags);
}
Action();
Refresh();
}
void CtrlPos::MouseMove(Point p, dword keyflags)
{
if(IsReadOnly() || !IsEnabled()) return;
moving = true;
pressed = (keyflags & K_MOUSELEFT);
if(ctrls.IsEmpty()) return;
Ctrl& c = *ctrls.Top(); //we are always editing top, even if more selected. they will be calculated accordingly
#if 1 //for instant showing where handle dragging is met
Rect r; //the rect of the ctrl
Rect _r; //the outer parent ctrl rect
GetAlignRects(c, *pctrl, r, _r);
int m = RectCtrl::GetMode(r, p, keyflags, style->handsize);
ci = RectCtrl::SetCursor(m, keyflags, ci);
#endif
if(pressed && mode != RectCtrl::NONE)
{
Point dp(p-xp);
if(keyflags & K_ALT)
{
//move into another parent to be found parent
//calculate some things not accessible when Removed()
//then, the Ctrls are removed to not take part in GetCtrl search as new destination
//then, ctrls are added to new destination or restored to old, if no new found
//r will be moving ctrl's source rect in pctrl frame coords ctx calculated from its origin parent
//then, the dp vector offset, from draggin the handles/moving is applied
//the new parent's ofset is calculated and applied
//finally, the new logpos is generated using old alignment values, but new rect and new parent's size
//cant use r = c.GetRect().Offseted(GetOffset(c, *pctrl));
//because parent changes as we move. would need to correct the xpos every time, what we dont want.
//so we calculate from the fixed base
Ctrl* q = c.GetParent(); //to determine later if we stayed within same parent
Vector<Ctrl*> _xpars;
Vector<Ctrl*> _xprevc;
_xpars.SetCount(ctrls.GetCount()); //keep track of current old parent
//_xprevc.SetCount(ctrls.GetCount()); //and the order
for(int i = 0; i < ctrls.GetCount(); i++)
{
Ctrl& _c = *ctrls[i];
_xpars[i] = _c.GetParent();
//_xprevc[i] = _c.GetPrev();
_c.Remove(); //prevent moving controls from finding when searching new parent
}
Point pt(p);
int ft(flags); flags |= (DEEP | NEST);
Ctrl* pc = GetCtrl(*pctrl, pt, flags, filter);
flags &= ~(DEEP | NEST); flags |= (ft & DEEP); //restore DEEP flag from save, NEST is ours
if(!pc) pc = pctrl; //enable fallback to global parent
if(pc && pc != q)
{
//add to new parent
for(int i = 0; i < ctrls.GetCount(); i++)
pc->AddChild(ctrls[i]);
}
else
{
//restore adding to previous
//we cant add to origin xpars here, since we need to keep track of the very last parrent (would oscilate)
//means that ctrls content from different parents ends up beeing added to ctrls.Top()'s parent,
for(int i = 0; i < ctrls.GetCount(); i++)
_xpars[i]->AddChild(ctrls[i]);//, xprevc[i]); //cant restore full order,
//since prev children may be moving as well and may not be added yet
#if 0
//restore order now would be possible
for(int i = 0; i < ctrls.GetCount(); i++)
_xpars[i]->AddChild(ctrls[i], _xprevc[i]);
#endif
}
//recalculate the new positions for all Ctrls
for(int i = 0; i < ctrls.GetCount(); i++)
{
Ctrl& c = *ctrls[i];
Rect r = xr[i];
RectCtrl::CalcRect(r, dp, keyflags, mode, g);
r.Normalize(); //in case r had been moved into flipped
//apply offsets
//transform ctrl's rect to pctrl context seen from old parent
//retransform to new parent
Point opd = GetOffset(c, *pctrl);
r.Offset(xop[i]-opd);
Ctrl::LogPos pos = LogPosPopUp::MakeLogPos(xpos[i], r, c.GetParent()->GetSize());
c.SetPos(pos);
}
}
else
{
//recalculate the new positions for all Ctrls
for(int i = 0; i < ctrls.GetCount(); i++)
{
Ctrl& c = *ctrls[i];
Rect r = xr[i];
RectCtrl::CalcRect(r, dp, keyflags, mode, g);
r.Normalize(); //in case r had been moved into flipped
Ctrl::LogPos pos = LogPosPopUp::MakeLogPos(xpos[i], r, c.GetParent()->GetSize());
c.SetPos(pos);
}
}
Action();
Refresh();
}
}
void CtrlPos::LeftUp(Point p, dword keyflags)
{
ReleaseCapture();
pressed = false;
moving = false;
xp.SetNull();
mode = RectCtrl::NONE;
if(IsReadOnly() || !IsEnabled()) return;
ci = RectCtrl::SetCursor(mode, keyflags, ci);
Refresh();
}
void CtrlPos::RightDown(Point p, dword keyflags)
{
ReleaseCapture();
pressed = (keyflags & K_MOUSELEFT);
moving = false;
xp.SetNull();
int m = mode;
mode = RectCtrl::NONE;
if(IsReadOnly() || !IsEnabled()) return;
ci = RectCtrl::SetCursor(mode, keyflags, ci);
if(ctrls.IsEmpty()) return;
if(pressed)
{
//cancel, restore backup of parent context and logpos
for(int i = 0; i < ctrls.GetCount(); i++)
{
if(ctrls[i]->GetParent() != xpars[i])
xpars[i]->Add(*ctrls[i]); //@todo: old order not kept
ctrls[i]->SetPos(xpos[i]);
}
Action();
Refresh();
}
else if(WhenBar)
MenuBar::Execute(WhenBar);
pressed = false;
}
void CtrlPos::MouseWheel(Point p, int zdelta, dword keyflags)
{
//if(!IsEditable()) return;
//if(!HasFocus()) SetFocus();
if(ctrls.IsEmpty()) return;
//change order of children for top only, drop remaining
ctrls[0] = ctrls.Top();
ctrls.SetCount(1);
Ctrl& c = *ctrls.Top();
if(ctrls.GetCount() > 1)
{
}
else
{
int i = zdelta/120;
for(; i<0; ++i)
{
Ctrl* p = c.GetNext();
c.GetParent()->AddChild(&c, p);
c.Refresh();
}
for(; i>0; --i)
{
Ctrl* p = c.GetPrev();
c.GetParent()->AddChildBefore(&c, p);
c.Refresh();
}
}
}
void CtrlPos::LeftDouble(Point p, dword flags)
{
//LeftDown already called
WhenLeftDouble();
}
bool CtrlPos::Key(dword key, int count)
{
if(key == K_SPACE)
{
ClearCtrl();
Action();
Refresh();
return true;
}
return false;
}
void CtrlPos::Updated()
{
//refresh the view of the currently selected ctrl
CtrlFinder::Updated();
}
CtrlPos::CtrlPos()
: pressed(false), moving(false), mode(RectCtrl::NONE), g(4,4)
{
BackPaint();
NoIgnoreMouse();
style = &RectCtrl::StyleDefault();
xp.SetNull();
}