#include "CtrlCore.h" NAMESPACE_UPP #define LLOG(x) // DLOG(x) #define LTIMING(x) // TIMING(x) void Ctrl::RefreshFrame(const Rect& r) { if(!IsOpen() || !IsVisible() || r.IsEmpty()) return; LLOG("RefreshRect " << Name() << ' ' << r); if(parent) { if(InFrame()) parent->RefreshFrame(r + GetRect().TopLeft()); else parent->Refresh(r + GetRect().TopLeft()); } else { LLOG("WndInvalidateRect: " << r << ' ' << Name()); WndInvalidateRect(r); #ifdef PLATFORM_WIN32 LLOG("UpdateRect: " << GetWndUpdateRect() << ' ' << Name()); #endif } } void Ctrl::Refresh(const Rect& area) { if(fullrefresh || !IsVisible() || !IsOpen()) return; LLOG("Refresh " << Name() << ' ' << area); RefreshFrame((area + GetView().TopLeft()) & GetView().Inflated(OverPaint())); } void Ctrl::Refresh() { if(fullrefresh || !IsVisible() || !IsOpen()) return; LLOG("Refresh " << Name() << " full:" << fullrefresh); Refresh(Rect(GetSize()).Inflated(OverPaint())); fullrefresh = true; } void Ctrl::Refresh(int x, int y, int cx, int cy) { Refresh(RectC(x, y, cx, cy)); } void Ctrl::RefreshFrame(int x, int y, int cx, int cy) { RefreshFrame(RectC(x, y, cx, cy)); } void Ctrl::RefreshFrame() { LLOG("RefreshFrame " << Name()); RefreshFrame(Rect(GetRect().Size()).Inflated(overpaint)); } void Ctrl::ScrollRefresh(const Rect& r, int dx, int dy) { if(!IsOpen() || !IsVisible() || r.IsEmpty()) return; int tdx = tabs(dx), tdy = tabs(dy); if(dx) WndInvalidateRect(RectC(dx >= 0 ? r.left : r.right - tdx, r.top - tdy, tdx, r.Height())); if(dy) WndInvalidateRect(RectC(r.left - tdx, dy >= 0 ? r.top : r.bottom - tdy, r.Width(), tdy)); } bool Ctrl::AddScroll(const Rect& sr, int dx, int dy) { if(!top) return true; for(int i = 0; i < top->scroll.GetCount(); i++) { Scroll& sc = top->scroll[i]; if(sc.rect == sr && sgn(dx) == sgn(sc.dx) && sgn(dy) == sgn(sc.dy)) { sc.dx += dx; sc.dy += dy; ScrollRefresh(sc.rect, sc.dx, sc.dy); return false; } if(sc.rect.Intersects(sr)) { sc.rect |= sr; sc.dx = sc.dy = 0; WndInvalidateRect(sc.rect); return true; } } Scroll& sc = top->scroll.Add(); sc.rect = sr; sc.dx = dx; sc.dy = dy; ScrollRefresh(sc.rect, sc.dx, sc.dy); return false; } Rect Ctrl::GetClippedView() { Rect sv = GetScreenView(); Rect view = sv; Ctrl *q = parent; Ctrl *w = this; while(q) { view &= w->InFrame() ? q->GetScreenRect() : q->GetScreenView(); w = q; q = q->parent; } return view - GetScreenRect().TopLeft(); } void Ctrl::ScrollView(const Rect& _r, int dx, int dy) { if(IsFullRefresh() || !IsVisible()) return; Size vsz = GetSize(); dx = sgn(dx) * min(abs(dx), vsz.cx); dy = sgn(dy) * min(abs(dy), vsz.cy); Rect r = _r & vsz; Ctrl *w = GetTopCtrl(); if(!w || !w->top) return; Rect view = InFrame() ? GetView() : GetClippedView(); Rect sr = (r + view.TopLeft()) & view; sr += GetScreenRect().TopLeft() - w->GetScreenRect().TopLeft(); if(w->AddScroll(sr, dx, dy)) Refresh(); else { Top *top = GetTopCtrl()->top; for(Ctrl *q = GetFirstChild(); q; q = q->GetNext()) if(q->InView()) { Rect cr = q->GetRect(); if(top && r.Intersects(cr)) { // Uno: Contains -> Intersetcs Rect to = cr; GetTopRect(to, false); if(r.Intersects(cr.Offseted(-dx, -dy))) { // Uno's suggestion 06/11/26 Contains -> Intersetcs Rect from = cr.Offseted(-dx, -dy); GetTopRect(from, false); MoveCtrl *m = FindMoveCtrlPtr(top->move, q); if(m && m->from == from && m->to == to) { LLOG("ScrollView Matched " << from << " -> " << to); m->ctrl = NULL; goto done; } } if(r.Intersects(cr.Offseted(dx, dy))) { // Uno's suggestion 06/11/26 Contains -> Intersetcs Rect from = to; to = cr.Offseted(dx, dy); GetTopRect(to, false); MoveCtrl& m = top->scroll_move.Add(q); m.from = from; m.to = to; m.ctrl = q; LLOG("ScrollView Add " << UPP::Name(q) << from << " -> " << to); goto done; } cr &= r; if(!cr.IsEmpty()) { Refresh(cr); Refresh(cr + Point(dx, dy)); } done:; } } } } void Ctrl::ScrollView(int x, int y, int cx, int cy, int dx, int dy) { ScrollView(RectC(x, y, cx, cy), dx, dy); } void Ctrl::ScrollView(int dx, int dy) { ScrollView(Rect(GetSize()), dx, dy); } void Ctrl::SyncScroll() { if(parent || !top) return; Vector scroll = top->scroll; top->scroll.Clear(); if(IsFullRefresh()) return; for(int i = 0; i < scroll.GetCount(); i++) { Scroll& sc = scroll[i]; if(abs(sc.dx) > 3 * sc.rect.Width() / 4 || abs(sc.dy) > 3 * sc.rect.Height() / 4) { LLOG("Sync scroll Invalidate rect" << sc.rect); WndInvalidateRect(sc.rect); } else if(sc.dx || sc.dy) { LLOG("WndScrollView " << sc.rect); WndScrollView(sc.rect, sc.dx, sc.dy); } } } Rect Ctrl::GetOpaqueRect() { return IsTransparent() ? Rect(0, 0, 0, 0) : GetSize(); } Rect Ctrl::GetVoidRect() { return Rect(0, 0, 0, 0); } #ifdef _DEBUG struct sDrawLevelCheck { int lvl; Draw& w; sDrawLevelCheck(Draw& w) : w(w), lvl(w.GetCloffLevel()) {} ~sDrawLevelCheck() { ASSERT(lvl == w.GetCloffLevel()); } }; #define LEVELCHECK(w) sDrawLevelCheck __(w) #else #define LEVELCHECK(w) #endif void Ctrl::CtrlPaint(Draw& w, const Rect& clip) { Rect rect = GetRect().GetSize(); Rect orect = rect.Inflated(overpaint); if(!IsShown() || orect.IsEmpty() || clip.IsEmpty() || !clip.Intersects(orect)) return; Ctrl *q; Rect view = rect; for(int i = 0; i < frame.GetCount(); i++) { LEVELCHECK(w); frame[i].frame->FramePaint(w, view); view = frame[i].view; } Rect oview = view.Inflated(overpaint); if(!view.IsEmpty()) { if(oview.Intersects(clip) && w.IsPainting(oview)) { LLOG("Painting: " << Name()); LEVELCHECK(w); if(overpaint) { w.Clip(oview); w.Offset(view.left, view.top); Paint(w); w.End(); w.End(); } else { w.Clipoff(view); Paint(w); w.End(); } } } bool hasviewctrls = false; for(q = firstchild; q; q = q->next) if(q->IsShown()) if(q->InFrame()) { LEVELCHECK(w); Point off = q->GetRect().TopLeft(); w.Offset(off); q->CtrlPaint(w, clip - off); w.End(); } else hasviewctrls = true; if(hasviewctrls && !view.IsEmpty()) { Rect cl = clip & view; w.Clip(cl); for(q = firstchild; q; q = q->next) if(q->IsShown() && q->InView()) { LEVELCHECK(w); Point off = q->GetRect().TopLeft() + view.TopLeft(); w.Offset(off); q->CtrlPaint(w, cl - off); w.End(); } w.End(); } } int sShowRepaint; void Ctrl::ShowRepaint(int q) { sShowRepaint = q; } void ShowRepaintRect(Draw& w, const Rect& r, Color c) { if(sShowRepaint) { w.DrawRect(r, c); Draw::Flush(); Sleep(sShowRepaint); } } bool Ctrl::PaintOpaqueAreas(Draw& w, Point offset, const Rect& clip) { Rect r = parent ? GetRect() + offset : GetRect().GetSize(); Point off = r.TopLeft(); Point viewpos = off + GetView().TopLeft(); if(!IsShown() || r.IsEmpty() || !r.Intersects(clip) || !w.IsPainting(r)) return true; if(backpaint == EXCLUDEPAINT) return w.ExcludeClip(r); Rect cview = clip & (GetView() + off); bool b = true; for(Ctrl *q = lastchild; q; q = q->prev) if(!q->PaintOpaqueAreas(w, q->InView() ? viewpos : off, q->InView() ? cview : clip)) return false; Rect opaque = (GetOpaqueRect() + viewpos) & clip; if(opaque.IsEmpty()) return true; if(backpaint == FULLBACKPAINT) { ShowRepaintRect(w, opaque, LtRed()); BackDraw bw; bw.Create(w, opaque.GetSize()); bw.Offset(viewpos - opaque.TopLeft()); bw.SetPaintingDraw(w, opaque.TopLeft()); { LEVELCHECK(bw); Paint(bw); } bw.Put(w, opaque.TopLeft()); } else { w.Clip(opaque); ShowRepaintRect(w, opaque, Green()); w.Offset(viewpos); { LEVELCHECK(w); Paint(w); } w.End(); w.End(); } LLOG("Exclude " << opaque); return w.ExcludeClip(opaque); } inline int Area(const Rect& r) { return r.GetHeight() * r.GetWidth(); } void CombineArea(Vector& area, const Rect& r) { if(r.IsEmpty()) return; int ra = Area(r); for(int i = 0; i < area.GetCount(); i++) { Rect ur = r | area[i]; int a = Area(ur); if(a < 2 * (ra + Area(area[i])) || a < 16000) { area[i] = ur; return; } } area.Add(r); } void Ctrl::GatherTransparentAreas(Vector& area, Draw& w, Point offset, const Rect& clip) { Rect r = parent ? GetRect() + offset : GetRect().GetSize(); Point off = r.TopLeft(); Point viewpos = off + GetView().TopLeft(); r.Inflate(overpaint); Rect notr = GetVoidRect(); if(notr.IsEmpty()) notr = GetOpaqueRect(); notr += viewpos; if(!IsShown() || r.IsEmpty() || !clip.Intersects(r) || !w.IsPainting(r)) return; if(notr.IsEmpty()) CombineArea(area, r & clip); else { if(notr != r) { CombineArea(area, clip & Rect(r.left, r.top, notr.left, r.bottom)); CombineArea(area, clip & Rect(notr.right, r.top, r.right, r.bottom)); CombineArea(area, clip & Rect(notr.left, r.top, notr.right, notr.top)); CombineArea(area, clip & Rect(notr.left, notr.bottom, notr.right, r.bottom)); } for(Ctrl *q = firstchild; q; q = q->next) q->GatherTransparentAreas(area, w, q->InView() ? viewpos : off, clip); } } void Ctrl::UpdateArea(Draw& draw, const Rect& clip) { if(IsPanicMode()) return; LTIMING("UpdateArea"); LLOG("========== UPDATE AREA " << UPP::Name(this) << " =========="); RemoveFullRefresh(); if(backpaint == FULLBACKPAINT) { ShowRepaintRect(draw, clip, LtRed()); BackDraw bw; bw.Create(draw, clip.GetSize()); bw.Offset(-clip.TopLeft()); bw.SetPaintingDraw(draw, clip.TopLeft()); CtrlPaint(bw, clip); bw.Put(draw, clip.TopLeft()); LLOG("========== END (FULLBACKPAINT)"); return; } if(backpaint == TRANSPARENTBACKPAINT) { LLOG("TransparentBackpaint"); Vector area; GatherTransparentAreas(area, draw, Point(0, 0), clip); for(int i = 0; i < area.GetCount(); i++) { Rect ar = area[i]; LLOG("Painting area: " << ar); ShowRepaintRect(draw, ar, LtBlue()); BackDraw bw; bw.Create(draw, ar.GetSize()); bw.Offset(-ar.TopLeft()); bw.SetPaintingDraw(draw, ar.TopLeft()); CtrlPaint(bw, ar); bw.Put(draw, ar.TopLeft()); if(!draw.ExcludeClip(ar)) { LLOG("========== END"); return; } } PaintOpaqueAreas(draw, Point(0, 0), clip); LLOG("========== END"); return; } CtrlPaint(draw, clip); LLOG("========== END"); } void Ctrl::RemoveFullRefresh() { fullrefresh = false; for(Ctrl *q = GetFirstChild(); q; q = q->GetNext()) q->RemoveFullRefresh(); } Ctrl *Ctrl::GetTopRect(Rect& r, bool inframe) { if(!inframe) r.Offset(GetView().TopLeft()); if(parent) { r.Offset(GetRect().TopLeft()); return parent->GetTopRect(r, InFrame()); } return this; } void Ctrl::DoSync(Ctrl *q, Rect r, bool inframe) { ASSERT(q); LLOG("DoSync " << UPP::Name(q) << " " << r); Ctrl *top = q->GetTopRect(r, inframe); top->SyncScroll(); top->WndUpdate(r); } void Ctrl::Sync() { LLOG("Sync " << Name()); if(!parent && IsOpen()) { LLOG("Sync UpdateWindow " << Name()); SyncScroll(); WndUpdate(); } else if(parent) DoSync(parent, GetRect(), inframe); SyncCaret(); } void Ctrl::Sync(const Rect& sr) { LLOG("Sync " << Name() << " " << sr); DoSync(this, sr, true); SyncCaret(); } void Ctrl::DrawCtrlWithParent(Draw& w, int x, int y) { if(parent) { Rect r = GetRect(); Ctrl *top = parent->GetTopRect(r, inframe); w.Clip(x, y, r.Width(), r.Height()); w.Offset(x - r.left, y - r.top); top->UpdateArea(w, r); w.End(); w.End(); } else DrawCtrl(w, x, y); } void Ctrl::DrawCtrl(Draw& w, int x, int y) { w.Offset(x, y); UpdateArea(w, GetRect().GetSize()); w.End(); } void Ctrl::SyncMoves() { if(parent || !top) return; for(int i = 0; i < top->move.GetCount(); i++) { MoveCtrl& m = top->move[i]; if(m.ctrl) { RefreshFrame(m.from); RefreshFrame(m.to); } } for(int i = 0; i < top->scroll_move.GetCount(); i++) { MoveCtrl& s = top->scroll_move[i]; if(s.ctrl) { RefreshFrame(s.from); RefreshFrame(s.to); } } top->move.Clear(); top->scroll_move.Clear(); } END_UPP_NAMESPACE