mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 06:05:58 -06:00
422 lines
8.6 KiB
C++
422 lines
8.6 KiB
C++
#include "Docking.h"
|
|
|
|
namespace Upp {
|
|
|
|
/*
|
|
int DockPane::ClientToPos(const Size& p)
|
|
{
|
|
int w = (width>>1)*pos.GetCount()-1;
|
|
return minmax(vert ? 10000 * p.cy / (GetSize().cy - w) : 10000 * p.cx / (GetSize().cx - w), 0, 9999);
|
|
}
|
|
*/
|
|
void DockPane::CumulativePos(Vector<int>& p) const
|
|
{
|
|
for (int i = 1; i < p.GetCount()-1; i++)
|
|
p[i] += p[i-1];
|
|
p.Top() = 10000;
|
|
}
|
|
|
|
void DockPane::NonCumulativePos(Vector<int>& p) const
|
|
{
|
|
for (int i = p.GetCount()-1; i > 0; i--)
|
|
p[i] -= p[i-1];
|
|
}
|
|
|
|
void DockPane::StartAnimate(int ix, Size sz, bool restore)
|
|
{
|
|
if (restore) {
|
|
ASSERT(savedpos.GetCount() == pos.GetCount());
|
|
animpos <<= savedpos;
|
|
}
|
|
else {
|
|
int tsz = ClientToPos(sz);
|
|
animpos <<= pos;
|
|
if (tsz) {
|
|
int msz = GetMinPos(ix);
|
|
if (msz < 10000 && msz+tsz > 10000) {
|
|
Ctrl *c = GetIndexChild(ix);
|
|
int min = ClientToPos(c->GetMinSize());
|
|
if (min < tsz) {
|
|
int std = ClientToPos(c->GetStdSize());
|
|
tsz = (std < tsz) ? std : min;
|
|
}
|
|
}
|
|
SmartReposUp(animpos, ix, tsz);
|
|
}
|
|
else
|
|
SmartReposDown(animpos, ix);
|
|
}
|
|
animtick = 0;
|
|
SetTimeCallback(-animinterval, THISBACK(AnimateTick), TIMEID_ANIMATE);
|
|
AnimateTick();
|
|
}
|
|
|
|
void DockPane::AnimateTick()
|
|
{
|
|
for (int i = 0; i < animpos.GetCount(); i++)
|
|
pos[i] += ((animpos[i] - pos[i]) * animtick) / animmaxticks;
|
|
animtick++;
|
|
if (animtick == animmaxticks)
|
|
EndAnimate();
|
|
else
|
|
Layout();
|
|
}
|
|
|
|
void DockPane::EndAnimate()
|
|
{
|
|
animtick = 0;
|
|
pos <<= animpos;
|
|
animpos.Clear();
|
|
KillTimeCallback(TIMEID_ANIMATE);
|
|
Remove(dummy);
|
|
/* if (dummy.GetParent()) {
|
|
pos.Remove(FindIndex(dummy));
|
|
dummy.Remove();
|
|
}*/
|
|
//FixChildSizes();
|
|
Layout();
|
|
}
|
|
|
|
void DockPane::SmartReposDown(Vector<int>& p, int ix)
|
|
{
|
|
int n = 0;
|
|
int dif = 0;
|
|
int maxsize = 0;
|
|
int totalsize = 0;
|
|
int sparesize = 0;
|
|
int cnt = p.GetCount();
|
|
Vector<int> maxpos;
|
|
maxpos.SetCount(cnt);
|
|
|
|
if (p.GetCount() == 1) {
|
|
p[0] = 0;
|
|
return;
|
|
}
|
|
|
|
// Convert pos to non-cumulative sizes
|
|
NonCumulativePos(p);
|
|
|
|
// Find max, and total sizes (in pos units)
|
|
for (Ctrl *c = GetFirstChild(); c; c = c->GetNext()) {
|
|
if (n != ix) {
|
|
maxpos[n] = max(ClientToPos(c->GetMaxSize()), p[n]);
|
|
maxsize += maxpos[n];
|
|
totalsize += p[n];
|
|
}
|
|
n++;
|
|
}
|
|
|
|
totalsize = max(totalsize, 1);
|
|
|
|
sparesize = p[ix];
|
|
p[ix] = 0;
|
|
for (int i = 0; i < cnt; i++)
|
|
p[i] += iscale(sparesize, p[i], totalsize);
|
|
|
|
// Restrict to max size
|
|
if (maxsize > 10000) {
|
|
totalsize = 0;
|
|
dif = 0;
|
|
for (int i = 0; i < cnt; i++)
|
|
if (i != ix) {
|
|
if (p[i] > maxpos[i]) {
|
|
dif += p[i] - maxpos[i];
|
|
p[i] = maxpos[i];
|
|
}
|
|
else
|
|
totalsize += p[i];
|
|
}
|
|
// Share out extra spare space to ctrls that are less than maxsize
|
|
while (dif > 1) {
|
|
int sum = 0;
|
|
for (int i = 0; i < cnt; i++) {
|
|
if (i != ix && p[i] < maxpos[i]) {
|
|
int t = max(1, min((p[i]*dif) / totalsize, maxpos[i] - p[i]));
|
|
p[i] += t;
|
|
sum += t;
|
|
}
|
|
}
|
|
dif -= sum;
|
|
}
|
|
}
|
|
|
|
// Do remainder
|
|
dif = Sum(p, 0) - 10000;
|
|
if (dif)
|
|
p[ix] += dif;
|
|
// Return to cumulative sizes
|
|
CumulativePos(p);
|
|
}
|
|
|
|
void DockPane::SmartReposUp(Vector<int>& p, int ix, int sz)
|
|
{
|
|
int n = 0;
|
|
int dif = 0;
|
|
int minsize = 0;
|
|
int maxsize = 0;
|
|
int totalsize = 0;
|
|
int cnt = p.GetCount();
|
|
int resizemin = max(10000 / (cnt*4), PosToClient(30));
|
|
Vector<int> minpos;
|
|
Vector<int> maxpos;
|
|
maxpos.SetCount(cnt);
|
|
minpos.SetCount(cnt);
|
|
|
|
// Convert pos to non-cumulative sizes
|
|
NonCumulativePos(p);
|
|
|
|
// Find min, max, and total sizes (in pos units)
|
|
for (Ctrl *c = GetFirstChild(); c; c = c->GetNext()) {
|
|
minpos[n] = min(ClientToPos(c->GetMinSize()), p[n]);
|
|
maxpos[n] = max(ClientToPos(c->GetMaxSize()), p[n]);
|
|
maxsize += maxpos[n];
|
|
if (n != ix)
|
|
minsize += minpos[n];
|
|
n++;
|
|
}
|
|
totalsize = 10000 - sz - minsize;
|
|
|
|
for (int i = 0; i < cnt; i++)
|
|
if (i != ix)
|
|
p[i] = minpos[i] + iscale(p[i], totalsize, 10000);
|
|
p[ix] = sz;
|
|
|
|
// Enforce an absolute minimum size
|
|
dif = 0;
|
|
minsize += minpos[ix];
|
|
for (int i = 0; i < cnt; i++) {
|
|
if (p[i] < resizemin && minpos[i] > p[i]) {
|
|
dif += resizemin - p[i];
|
|
p[i] = resizemin;
|
|
}
|
|
}
|
|
if (dif) {
|
|
totalsize = 10000 - cnt*resizemin;
|
|
for (int i = 0; i < cnt; i++) {
|
|
int t = p[i] - resizemin;
|
|
if (t > 0)
|
|
p[i] -= (t*dif) / totalsize;
|
|
}
|
|
}
|
|
|
|
// Restrict to max size
|
|
if (maxsize > 10000) {
|
|
totalsize = 0;
|
|
dif = 0;
|
|
for (int i = 0; i < cnt; i++)
|
|
if (i != ix) {
|
|
if (p[i] > maxpos[i]) {
|
|
dif += p[i] - maxpos[i];
|
|
p[i] = maxpos[i];
|
|
}
|
|
else
|
|
totalsize += p[i];
|
|
}
|
|
else if (p[i] < maxpos[i])
|
|
totalsize += p[i];
|
|
// Share out extra spare space to ctrls that are less than maxsize
|
|
while (dif > 1) {
|
|
int sum = 0;
|
|
for (int i = 0; i < cnt; i++) {
|
|
if (p[i] < maxpos[i]) {
|
|
int t = max(1, min((p[i]*dif) / totalsize, maxpos[i] - p[i]));
|
|
p[i] += t;
|
|
sum += t;
|
|
}
|
|
}
|
|
dif -= sum;
|
|
}
|
|
}
|
|
|
|
// Do remainder
|
|
dif = Sum(p, 0) - 10000;
|
|
if (dif)
|
|
p[ix] += dif;
|
|
// Return to cumulative sizes
|
|
CumulativePos(p);
|
|
}
|
|
|
|
void DockPane::SimpleRepos(Vector<int>& p, int ix, int inc)
|
|
{
|
|
int cnt = p.GetCount();
|
|
if (cnt == 1) {
|
|
p[0] = 10000;
|
|
return;
|
|
}
|
|
|
|
// Convert pos to non-cumulative sizes
|
|
NonCumulativePos(p);
|
|
|
|
int n = 0;
|
|
int tsz = 0;
|
|
|
|
for (Ctrl *c = GetFirstChild(); c; c = c->GetNext()) {
|
|
if (n != ix)
|
|
tsz += p[n];
|
|
n++;
|
|
}
|
|
int dif = tsz - inc;
|
|
int sum = 0;
|
|
p[ix] += inc;
|
|
for (int i = 0; i < cnt; i++) {
|
|
if (i != ix)
|
|
p[i] = (p[i]*dif) / tsz ;
|
|
sum += p[i];
|
|
}
|
|
dif = sum - 10000;
|
|
if (dif)
|
|
p[ix] += dif;
|
|
// Return to cumulative sizes
|
|
CumulativePos(p);
|
|
}
|
|
|
|
int DockPane::GetMinPos(int notix)
|
|
{
|
|
int n = 0;
|
|
int msz = 0;
|
|
for (Ctrl *c = GetFirstChild(); c; c = c->GetNext()) {
|
|
if (n != notix) msz += ClientToPos(c->GetMinSize());
|
|
n++;
|
|
}
|
|
return msz;
|
|
}
|
|
|
|
void DockPane::FixChildSizes()
|
|
{
|
|
int cnt = pos.GetCount();
|
|
if (!cnt) return;
|
|
if (cnt == 1) {
|
|
pos[0] = 10000;
|
|
return;
|
|
}
|
|
int sum = pos[0];
|
|
for (int i = 1; i < cnt; i++)
|
|
sum += pos[i] - pos[i-1];
|
|
sum -= 10000;
|
|
int rem = sum % cnt;
|
|
sum /= cnt;
|
|
if (sum)
|
|
for (int i = 0; i < cnt; i++)
|
|
pos[i] -= sum*(i+1);
|
|
pos[cnt-1] -= rem;
|
|
ASSERT(pos[cnt-1] == 10000);
|
|
}
|
|
|
|
/*
|
|
int DockPane::FindIndex(Ctrl& child)
|
|
{
|
|
int ix = 0;
|
|
for (Ctrl *c = GetFirstChild(); c; c = c->GetNext()) {
|
|
if (c == &child) return ix;
|
|
ix++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
Ctrl * DockPane::FindCtrl(int ix)
|
|
{
|
|
Ctrl *c = GetFirstChild();
|
|
for (int i = 0; i < ix && c; i++)
|
|
c = c->GetNext();
|
|
return c;
|
|
}
|
|
|
|
void DockPane::Swap(Ctrl& child, Ctrl& newctrl)
|
|
{
|
|
newctrl.SetRect(child.GetRect());
|
|
Ctrl::AddChildBefore(&newctrl, &child);
|
|
Ctrl::RemoveChild(&child);
|
|
}
|
|
*/
|
|
void DockPane::Dock(Ctrl& newctrl, Size sz, int ps, bool animate, bool save)
|
|
{
|
|
if (IsAnimating())
|
|
EndAnimate();
|
|
|
|
int cnt = pos.GetCount();
|
|
if (!cnt) animate = false;
|
|
int tsz = cnt ? ClientToPos(sz) : 10000;
|
|
ps = min(ps, cnt);
|
|
if (ps >= 0) {
|
|
if (ps && ps == cnt)
|
|
Ctrl::AddChild(&newctrl, GetLastChild());
|
|
else {
|
|
Ctrl *c = GetFirstChild();
|
|
for (int i = 0; i < ps; i++)
|
|
c = c->GetNext();
|
|
Ctrl::AddChildBefore(&newctrl, c);
|
|
}
|
|
pos.Insert(ps);
|
|
pos[ps] = (ps > 0) ? pos[ps-1] : 0;
|
|
}
|
|
else {
|
|
Splitter::Add(newctrl);
|
|
Splitter::pos.Add(0);
|
|
ps = cnt;
|
|
if (ps > 0) pos[ps] = pos[ps-1];
|
|
}
|
|
if (save)
|
|
SavePos();
|
|
if (animate)
|
|
StartAnimate(ps, sz, false);
|
|
else {
|
|
if (cnt)
|
|
SmartReposUp(pos, ps, tsz);
|
|
else
|
|
pos[ps] = 10000;
|
|
Layout();
|
|
}
|
|
}
|
|
|
|
void DockPane::Undock(Ctrl& child, bool animate, bool restore)
|
|
{
|
|
if (IsAnimating())
|
|
EndAnimate();
|
|
|
|
int ix = GetChildIndex(&child);
|
|
if (animate && GetFirstChild() != GetLastChild()) {
|
|
dummy.Remove();
|
|
Swap(child, dummy);
|
|
StartAnimate(ix, Size(0, 0), restore);
|
|
}
|
|
else {
|
|
if (restore)
|
|
RestorePos();
|
|
SmartReposDown(pos, ix);
|
|
child.Remove();
|
|
pos.Remove(ix);
|
|
Layout();
|
|
}
|
|
}
|
|
|
|
Rect DockPane::GetFinalAnimRect(Ctrl& ctrl)
|
|
{
|
|
ASSERT(ctrl.GetParent() == this);
|
|
if (!IsAnimating())
|
|
return ctrl.GetRect();
|
|
int ix = GetChildIndex(&ctrl);
|
|
ASSERT(ix >= 0 && ix < animpos.GetCount());
|
|
|
|
Rect r = GetRect();
|
|
int prev = ix ? animpos[ix-1] : 0;
|
|
int width = GetSplitWidth();
|
|
if (IsHorz()) {
|
|
r.left += PosToClient(prev) + width*(ix);
|
|
r.right = r.left + PosToClient(animpos[ix] - prev);
|
|
}
|
|
else {
|
|
r.top += PosToClient(prev) + width*(ix);
|
|
r.bottom = r.top + PosToClient(animpos[ix] - prev);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
DockPane::DockPane()
|
|
{
|
|
animtick = 0;
|
|
animinterval = 20; // milliseconds
|
|
animmaxticks = 10;
|
|
pos.Clear();
|
|
}
|
|
|
|
}
|