ultimatepp/uppsrc/CtrlLib/Splitter.cpp
cxl cf12a89899 Splitter: Improved handling of minimal size, TabCtrl::Compute
git-svn-id: svn://ultimatepp.org/upp/trunk@11547 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2017-12-11 16:44:52 +00:00

302 lines
5.7 KiB
C++

#include "CtrlLib.h"
namespace Upp {
CH_STYLE(Splitter, Style, StyleDefault)
{
width = Ctrl::HorzLayoutZoom(4);
vert[0] = horz[0] = SColorFace();
vert[1] = horz[1] = GUI_GlobalStyle() >= GUISTYLE_XP ? Blend(SColorHighlight, SColorFace)
: SColorShadow();
dots = true;
}
int Splitter::ClientToPos(Point p) const
{
return minmax(vert ? 10000 * p.y / GetSize().cy : 10000 * p.x / GetSize().cx, 0, 9999);
}
int Splitter::PosToClient(int pos) const
{
return (vert ? GetSize().cy : GetSize().cx) * pos / 10000;
}
void Splitter::Layout() {
Size sz = GetSize();
int count = GetChildCount();
if(count == 0)
return;
mins.SetCount(count, 0);
minpx.SetCount(count, 0);
count--;
if(pos.GetCount() < count) {
pos.SetCount(count, 0);
for(int i = 0; i < count; i++)
pos[i] = (i + 1) * 10000 / (count + 1);
}
if(style >= 0) {
int i = 0;
for(Ctrl *q = GetFirstChild(); q; q = q->GetNext()) {
if(style == i)
q->SizePos().Show();
else
q->Hide();
i++;
}
return;
}
int lw = chstyle->width >> 1;
int rw = chstyle->width - lw;
int i = 0;
for(Ctrl *q = GetFirstChild(); q; q = q->GetNext()) {
int lo = i > 0 ? PosToClient(pos[i - 1]) + rw : 0;
int hi = i < count ? PosToClient(pos[i]) - lw : vert ? sz.cy : sz.cx;
q->Show();
if(vert)
q->SetRect(0, lo, sz.cx, hi - lo);
else
q->SetRect(lo, 0, hi - lo, sz.cy);
i++;
}
}
int Splitter::GetSplitWidth() const
{
return chstyle->width;
}
void Splitter::PaintDots(Draw& w, const Rect& r, bool vert)
{
int x = r.left + r.GetWidth() / 2;
int y = r.top + r.GetHeight() / 2;
(vert ? x : y) -= 8 * 4;
for(int i = 0; i < 16; i++) {
w.DrawRect(x - 1, y - 1, 1, 1, SColorShadow());
w.DrawRect(x, y, 1, 1, SColorShadow());
(vert ? x : y) += 4;
}
}
void Splitter::Paint(Draw& w) {
Size sz = GetSize();
if(!IsTransparent())
w.DrawRect(sz, SColorFace);
const Value *ch = vert ? chstyle->vert : chstyle->horz;
if(style < 0)
for(int i = 0; i < pos.GetCount(); i++) {
int p = PosToClient(pos[i]) - (chstyle->width >> 1);
Rect r = vert ? RectC(0, p, sz.cx, chstyle->width) : RectC(p, 0, chstyle->width, sz.cy);
if(HasCapture() && i == mouseindex)
ChPaint(w, r, ch[1]);
else
if(!IsTransparent())
ChPaint(w, r, ch[0]);
if(chstyle->dots)
PaintDots(w, r, vert);
}
}
void Splitter::MouseMove(Point p, dword) {
if(HasCapture() && mouseindex >= 0 && mouseindex < pos.GetCount()) {
SetPos(ClientToPos(p), mouseindex);
Refresh();
WhenAction();
}
}
void Splitter::LeftDown(Point p, dword) {
SetCapture();
Refresh();
mouseindex = FindIndex(p);
}
int Splitter::FindIndex(Point client) const {
int best = -1;
int maxdist = chstyle->width;
for(int i = 0; i < pos.GetCount(); i++) {
int dist = abs((vert ? client.y : client.x) - PosToClient(pos[i]));
if(dist <= maxdist) {
best = i;
maxdist = abs(dist);
}
}
return best;
}
void Splitter::LeftUp(Point p, dword keyflags) {
if(HasCapture())
WhenSplitFinish();
ReleaseCapture();
Refresh();
}
Image Splitter::CursorImage(Point p, dword) {
return FindIndex(p) < 0 ? Image::Arrow() : vert ? Image::SizeVert() : Image::SizeHorz();
}
int Splitter::GetMins(int i) const
{
int min1 = (i < mins.GetCount() ? mins[i] : 0);
int min2 = 0;
int cx = IsVert() ? GetSize().cy : GetSize().cx;
if(cx)
min2 = (i < minpx.GetCount() ? minpx[i] : 0) * 10000 / cx;
return max(min1, min2);
}
Splitter& Splitter::SetPos(int p, int i) {
int l = (i > 0 && i - 1 < pos.GetCount() ? pos[i - 1] : 0) + GetMins(i);
int h = (i + 1 < pos.GetCount() ? pos[i + 1] : 10000) - GetMins(i + 1);
pos.At(i) = minmax(p, l, h);
Layout();
return *this;
}
void Splitter::SyncMin()
{
for(int i = 0; i < pos.GetCount(); i++)
SetPos(pos[i], i);
}
void Splitter::Zoom(int i)
{
style = i;
Layout();
}
Splitter& Splitter::Vert(Ctrl& top, Ctrl& bottom)
{
vert = true;
Set(top, bottom);
return *this;
}
Splitter& Splitter::Horz(Ctrl& left, Ctrl& right)
{
vert = false;
Set(left, right);
return *this;
}
void Splitter::Set(Ctrl& l, Ctrl& r)
{
Clear();
*this << l << r;
Layout();
}
void Splitter::Add(Ctrl& pane)
{
Ctrl::Add(pane);
Layout();
}
void Splitter::Insert(int ii, Ctrl& pane)
{
if(ii >= GetCount())
Add(pane);
else {
Ctrl::AddChildBefore(&pane, GetIndexChild(ii));
pos.Clear();
Layout();
}
}
void Splitter::Swap(Ctrl& child, Ctrl& newctrl)
{
newctrl.SetRect(child.GetRect());
Ctrl::AddChildBefore(&newctrl, &child);
Ctrl::RemoveChild(&child);
}
void Splitter::Serialize(Stream& s) {
int version = 0x02;
s / version;
if(version <= 1) {
int p = GetPos(0);
s % p;
SetPos(p, 0);
}
else
s % pos;
if(version >= 0x01) {
s / style;
if(version == 0x01 && style == 3)
style = -1;
}
if(s.IsLoading()) {
for(int i = 0; i < pos.GetCount(); i++)
if(pos[i] < 0 || pos[i] > 10000) {
pos.Clear();
s.LoadError();
}
if(style >= GetChildCount()) {
style = -1;
s.LoadError();
}
Layout();
}
}
void Splitter::Remove(Ctrl& ctrl)
{
int n = 0;
Ctrl *c = GetFirstChild();
while(c){
if(c == &ctrl){
if(c->GetNext())
pos.Remove(n);
else
if(n >= 1)
pos.Remove(n-1);
mins.Remove(n);
minpx.Remove(n);
RemoveChild(c);
break;
}
c = c->GetNext();
n++;
}
}
void Splitter::Clear() {
while(GetFirstChild())
RemoveChild(GetFirstChild());
pos.Clear();
mins.Clear();
minpx.Clear();
}
void Splitter::Reset() {
Clear();
style = -1;
vert = false;
}
Splitter& Splitter::SetStyle(const Style& s)
{
if(chstyle != &s) {
chstyle = &s;
RefreshLayout();
Refresh();
}
return *this;
}
Splitter::Splitter() {
chstyle = NULL;
style = -1;
vert = false;
mouseindex = -1;
pos.Add(5000);
SetFrame(NullFrame());
NoWantFocus();
VSizePos(0, 0).HSizePos(0, 0);
SetStyle(StyleDefault());
}
Splitter::~Splitter() {}
}