diff --git a/src/common/Widgets/TabView.cpp b/src/common/Widgets/TabView.cpp --- a/src/common/Widgets/TabView.cpp +++ b/src/common/Widgets/TabView.cpp @@ -1,1534 +1,1534 @@ /*hdr ** FILE: LTabView.cpp ** AUTHOR: Matthew Allen ** DATE: 20/10/2000 ** DESCRIPTION: Lgi self-drawn tab control ** ** Copyright (C) 2000 Matthew Allen ** fret@memecode.com */ #include #include "lgi/common/Lgi.h" #include "lgi/common/TabView.h" #include "lgi/common/SkinEngine.h" #include "lgi/common/DisplayString.h" #include "lgi/common/TableLayout.h" #include "lgi/common/LgiRes.h" #include "lgi/common/CssTools.h" #include "lgi/common/Path.h" enum TabViewStyle { TvWinXp, // Skin TvWin7, TvMac, }; #define MAC_STYLE_RADIUS 7 #define MAC_DBL_BUF 1 #if defined(__GTK_H__) #define TAB_TXT_PAD 2 #else #define TAB_TXT_PAD 3 #endif #if defined(MAC) && !LGI_COCOA && !defined(LGI_SDL) #define MAC_PAINT 1 #else #define MAC_PAINT 0 #endif #define TAB_MARGIN_X 10 // Px each side of the text label on the tab #define CLOSE_BTN_SIZE 8 #define CLOSE_BTN_GAP 8 #define cFocusFore LColour(L_FOCUS_SEL_FORE) #define cFocusBack LColour(L_FOCUS_SEL_BACK) //////////////////////////////////////////////////////////////////////////////////////////// class LTabViewPrivate { public: // General int Current; LRect TabClient; bool PourChildren; // Painting LRect Inset, Tabs; int TabsHeight; double TabsBaseline; int Depth; TabViewStyle Style; enum ResType { ResWorkspace, ResSelectedUnfocused, ResSelectedFocused, ResMax }; LAutoPtr Corners[ResMax]; LColour cBack, cBorder, cFill, cSelUnfoc, cTopEdge, cBottomEdge; // Scrolling int Scroll; // number of buttons scrolled off the left of the control LRect LeftBtn; // scroll left button LRect RightBtn; // scroll right button LTabViewPrivate() { Depth = 0; TabsHeight = 0; TabsBaseline = 0.0; PourChildren = true; Current = 0; TabClient.ZOff(-1, -1); Scroll = 0; LeftBtn.ZOff(-1, -1); RightBtn.ZOff(-1, -1); Style = TvMac; } uint8_t Clamp(int i) { if (i < 0) return 0; if (i > 255) return 255; return (uint8_t)i; } LColour Tint(double amt) { auto Bk = cBack.GetGray(); double Ratio = Bk < 100 ? 1.0 / amt : amt; LColour c( Clamp((int)(Ratio * cBack.r())), Clamp((int)(Ratio * cBack.g())), Clamp((int)(Ratio * cBack.b()))); return c; } bool DrawCircle(LAutoPtr &Dc, LColour c) { if (Dc) return true; double r = 7.0; int x = (int)(r * 2.0); if (!Dc.Reset(new LMemDC(x, x, System32BitColourSpace))) return false; Dc->Colour(0, 32); Dc->Rectangle(); LPath p; p.Circle(r, r, r-0.7); p.SetFillRule(FILLRULE_ODDEVEN); LSolidBrush s(c); p.Fill(Dc, s); p.Empty(); p.Circle(r, r, r); p.Circle(r, r, r - 1.0); p.SetFillRule(FILLRULE_ODDEVEN); LBlendStop Stops[2] = { {0.0, cTopEdge.c32()}, {1.0, cBottomEdge.c32()} }; LPointF a(4, 4), b(9, 9); LLinearBlendBrush s2(a, b, CountOf(Stops), Stops); p.Fill(Dc, s2); #ifndef WINDOWS if (Dc->IsPreMultipliedAlpha()) #endif Dc->ConvertPreMulAlpha(true); return true; } void CreateCorners() { LAutoPtr &White = Corners[ResWorkspace]; LAutoPtr &Unfoc = Corners[ResSelectedUnfocused]; LAutoPtr &Sel = Corners[ResSelectedFocused]; DrawCircle(White, LColour(L_WORKSPACE)); DrawCircle(Unfoc, cSelUnfoc); DrawCircle(Sel, cFocusBack); } }; struct LTabPagePriv { LTabPage *Tab; bool NonDefaultFont; LAutoPtr Ds; LTabPagePriv(LTabPage *t) : Tab(t) { NonDefaultFont = false; } LDisplayString *GetDs() { auto Text = Tab->Name(); if (Text && !Ds) { LFont *f = NULL; auto s = Tab->GetCss(); NonDefaultFont = s ? s->HasFontStyle() : false; if (NonDefaultFont) { if ((f = new LFont)) { *f = *LSysFont; if (f->CreateFromCss(s)) Tab->SetFont(f, true); else DeleteObj(f); } } else { f = Tab->GetFont(); } if (f) Ds.Reset(new LDisplayString(f, Text)); else LAssert(!"no font."); } return Ds; } }; class TabIterator : public LArray { public: TabIterator(List &l) { for (auto c : l) { auto p = dynamic_cast(c); if (p) Add(p); } fixed = true; } }; //////////////////////////////////////////////////////////////////////////////////////////// // Tab Control LTabView::LTabView(int id, int x, int y, int cx, int cy, const char *name, int Init) : ResObject(Res_TabView) { d = new LTabViewPrivate; d->Current = Init; SetId(id); LRect r(x, y, x+cx, y+cy); SetPos(r); Name(name); _BorderSize = 0; SetTabStop(true); SetPourLargest(true); #if WINNATIVE SetDlgCode(DLGC_WANTARROWS); #endif } LTabView::~LTabView() { DeleteObj(d); } bool LTabView::GetPourChildren() { return d->PourChildren; } void LTabView::SetPourChildren(bool b) { d->PourChildren = b; } LTabPage *LTabView::TabAt(int Idx) { TabIterator i(Children); return i[Idx]; } size_t LTabView::GetTabs() { return Children.Length(); } LTabPage *LTabView::GetCurrent() { TabIterator i(Children); return i[d->Current]; } int LTabView::TabY() { return d->TabsHeight + (TAB_TXT_PAD << 1); } void LTabView::OnChildrenChanged(LViewI *Wnd, bool Attaching) { if (!Attaching) { TabIterator c(Children); if (d->Current >= c.Length()) d->Current = (int)c.Length() - 1; #if LGI_VIEW_HANDLE if (Handle()) #endif Invalidate(); } } #if defined(WINNATIVE) LViewI *LTabView::FindControl(HWND hCtrl) { LViewI *Ctrl = 0; if (hCtrl == Handle()) { return this; } TabIterator it(Children); for (int i=0; iFindControl(hCtrl)) return Ctrl; } return 0; } #endif LViewI *LTabView::FindControl(int Id) { if (GetId() == Id) { return this; } LViewI *Ctrl; TabIterator it(Children); for (int i=0; iFindControl(Id))) return Ctrl; } return 0; } bool LTabView::Attach(LViewI *parent) { bool Status = LView::Attach(parent); if (Status) { TabIterator it(Children); LTabPage *p = d->Current < it.Length() ? it[d->Current] : 0; if (p) { OnPosChange(); p->Attach(this); } for (int i=0; i_Window = _Window; } } return Status; } int64 LTabView::Value() { return d->Current; } void LTabView::OnCreate() { LResources::StyleElement(this); d->Depth = 0; LViewI *p = this; while ((p = p->GetParent())) { if (p == (LViewI*)GetWindow()) break; LTabView *tv = dynamic_cast(p); if (tv) d->Depth++; } OnStyleChange(); TabIterator it(Children); LTabPage *page = d->Current < it.Length() ? it[d->Current] : 0; if (page) { page->Attach(this); page->Visible(true); } } void LTabView::Value(int64 i) { if (Children.Length() > 0 && i != d->Current) { // change tab TabIterator it(Children); LTabPage *Old = it[d->Current]; if (Old) { // printf("%s:%i - old[%i] hide.\n", _FL, d->Current); Old->Visible(false); } else printf("%s:%i - no old.\n", _FL); d->Current = (int)MIN(i, (ssize_t)it.Length()-1); OnPosChange(); LTabPage *p = it[d->Current]; - if (p) + if (p && IsAttached()) { if (!p->IsAttached()) { // printf("%s:%i - new[%i] attach %p.\n", _FL, d->Current, p->Handle()); p->Attach(this); } // printf("%s:%i - new[%i] visible %p.\n", _FL, d->Current, p->Handle()); p->Visible(true); } Invalidate(); LNotification n(LNotifyValueChanged, _FL); n.Int[0] = d->Current; SendNotify(n); // GetWindow()->_Dump(); } } LMessage::Result LTabView::OnEvent(LMessage *Msg) { return LView::OnEvent(Msg); } int LTabView::OnNotify(LViewI *Ctrl, LNotification n) { if (GetParent()) { return GetParent()->OnNotify(Ctrl, n); } return 0; } bool LTabView::Append(LTabPage *Page, int Where) { if (Page) { Page->TabCtrl = this; Page->_Window = _Window; if (IsAttached() && Children.Length() == 1) { Page->Attach(this); OnPosChange(); } else { Page->Visible(Children.Length() == d->Current); AddView(Page); } Invalidate(); return true; } return false; } LTabPage *LTabView::Append(const char *name, int Where) { LTabPage *Page = new LTabPage(name); if (Page) { Page->TabCtrl = this; Page->_Window = _Window; Page->SetParent(this); if (IsAttached() && Children.Length() == 0) { Page->Attach(this); OnPosChange(); } else { Page->Visible(Children.Length() == d->Current); AddView(Page); } Invalidate(); } return Page; } bool LTabView::Delete(LTabPage *Page) { bool Status = false; TabIterator it(Children); ssize_t Index = it.IndexOf(Page); if (Index >= 0) { if (Index == d->Current) { Status = Page->Detach(); LAssert(Status); } else { Status = DelView(Page); LAssert(Status); } delete Page; Value(Index); } return Status; } LRect <abView::GetTabClient() { if (d->Style == TvMac) { d->TabClient = CalcInset(); d->TabClient.Inset(2, 2); // The inset border d->TabClient.y1 = d->Tabs.y2 + 1; // The tab strip LTabPage *p = Children.Length() ? GetCurrent() : NULL; if (p && p->GetCss()) { // Inset by any padding LCss::Len l; d->TabClient.x1 += (l = p->GetCss()->PaddingLeft()).IsValid() ? l.ToPx(d->TabClient.X(), GetFont()) : 0; d->TabClient.y1 += (l = p->GetCss()->PaddingTop()).IsValid() ? l.ToPx(d->TabClient.Y(), GetFont()) : 0; d->TabClient.x2 -= (l = p->GetCss()->PaddingRight()).IsValid() ? l.ToPx(d->TabClient.X(), GetFont()) : 0; d->TabClient.y2 -= (l = p->GetCss()->PaddingBottom()).IsValid() ? l.ToPx(d->TabClient.Y(), GetFont()) : 0; } } else { d->TabClient = LView::GetClient(); d->TabClient.Offset(-d->TabClient.x1, -d->TabClient.y1); d->TabClient.Inset(2, 2); d->TabClient.y1 += TabY(); } return d->TabClient; } int LTabView::HitTest(LMouse &m) { if (d->LeftBtn.Overlap(m.x, m.y)) { return LeftScrollBtn; } else if (d->RightBtn.Overlap(m.x, m.y)) { return RightScrollBtn; } else { // int Hit = -1; TabIterator it(Children); for (int i=0; iTabPos.Overlap(m.x, m.y)) return i; } } return NoBtn; } void LTabView::OnMouseClick(LMouse &m) { bool DownLeft = m.Down() || m.Left(); int Result = HitTest(m); if (m.Down()) Focus(true); if (Result == LeftScrollBtn) { if (DownLeft) { d->Scroll++; Invalidate(); } } else if (Result == RightScrollBtn) { if (DownLeft) { d->Scroll = MAX(0, d->Scroll-1); Invalidate(); } } else if (Result >= 0) { TabIterator it(Children); LTabPage *p = it[Result]; if (p) { if (p->HasButton() && p->BtnPos.Overlap(m)) { if (DownLeft) { p->OnButtonClick(m); // The tab can delete itself after this event return; } } else { // We set this before firing the event, otherwise the // code seeing the notication gets the old value. if (DownLeft) Value(Result); p->OnTabClick(m); } } } if (DownLeft) Focus(true); } bool LTabView::OnKey(LKey &k) { if (k.Down()) { switch (k.vkey) { case LK_LEFT: { if (k.Alt()) break; if (d->Current > 0) { TabIterator it(Children); LTabPage *p = it[d->Current - 1]; if (p && !p->TabPos.Valid()) { if (d->Scroll) d->Scroll--; } Value(d->Current - 1); } return true; break; } case LK_RIGHT: { if (k.Alt()) break; TabIterator it(Children); if (d->Current < it.Length() - 1) { LTabPage *p = it[d->Current + 1]; if (p && !p->TabPos.Valid()) { d->Scroll++; } Value(d->Current + 1); } return true; break; } } } return false; } void LTabView::OnFocus(bool f) { if (!Children.Length()) return; TabIterator it(Children); LTabPage *p = it[d->Current]; if (p) { LRect r = p->TabPos; r.y2 += 2; Invalidate(&r); } } void LTabView::OnAttach() { } LRect <abView::CalcInset() { LRect Padding(0, 0, 0, 0); d->Inset = GetClient(); auto f = GetFont(); if (GetCss()) { LCss::Len l; if ((l = GetCss()->PaddingLeft()).IsValid()) Padding.x1 = l.ToPx(d->Inset.X(), f); if ((l = GetCss()->PaddingTop()).IsValid()) Padding.y1 = l.ToPx(d->Inset.Y(), f); if ((l = GetCss()->PaddingRight()).IsValid()) Padding.x2 = l.ToPx(d->Inset.X(), f); if ((l = GetCss()->PaddingBottom()).IsValid()) Padding.y2 = l.ToPx(d->Inset.Y(), f); } int TabTextY = 0; d->TabsBaseline = 0; TabIterator Tabs(Children); for (auto t : Tabs) { auto Ds = t->d->GetDs(); if (Ds) { TabTextY = MAX(TabTextY, Ds->Y()); auto Fnt = Ds->GetFont(); d->TabsBaseline = MAX(d->TabsBaseline, Fnt->Ascent()); } } if (!TabTextY) TabTextY = f->GetHeight(); d->TabsHeight = TabTextY; d->Inset.x1 += Padding.x1; d->Inset.x2 -= Padding.x2; d->Inset.y1 += Padding.y1; d->Inset.y2 -= Padding.y2; d->Tabs.ZOff(d->Inset.X() - 20, TabY() - 1); d->Tabs.Offset(d->Inset.x1 + 10, d->Inset.y1); d->Inset.y1 = d->Tabs.y1 + (d->Tabs.Y() / 2); return d->Inset; } void LTabView::OnStyleChange() { if (!d->cBack.IsValid()) { LCssTools Tools(this); d->cBack = Tools.GetBack(); d->cTopEdge = d->Tint(0.845); d->cBottomEdge = d->Tint(0.708); d->cSelUnfoc = LColour(L_NON_FOCUS_SEL_BACK); auto mul = pow(0.909f, 1+d->Depth); // 240->218 d->cBorder = d->Tint(mul); mul = pow(0.959f, 1+d->Depth); // 240->230 d->cFill = d->Tint(mul); auto *Css = GetCss(true); if (Css) { if (!Css->BackgroundColor().IsValid()) Css->BackgroundColor(LCss::ColorDef(d->cFill)); } d->CreateCorners(); } TabIterator Tabs(Children); for (auto t : Tabs) t->OnStyleChange(); Invalidate(); } void LTabView::OnPaint(LSurface *pDC) { #ifdef WINDOWS // FIXME: looks like crap but doesn't flicker. LDoubleBuffer Dbl(pDC); #endif if (!d->cBack.IsValid()) OnStyleChange(); LCssTools Tools(this); TabIterator it(Children); if (d->Current >= it.Length()) Value(it.Length() - 1); if (d->Style == TvMac) { CalcInset(); LView *Pv = GetParent() ? GetParent()->GetGView() : NULL; LColour NoPaint = (Pv ? Pv : this)->StyleColour(LCss::PropBackgroundColor, LColour(L_MED)); if (!NoPaint.IsTransparent()) { pDC->Colour(NoPaint); pDC->Rectangle(); } #ifdef LGI_CARBON HIRect Bounds = d->Inset; HIThemeTabPaneDrawInfo Info; Info.version = 1; Info.state = Enabled() ? kThemeStateActive : kThemeStateInactive; Info.direction = kThemeTabNorth; Info.size = kHIThemeTabSizeNormal; Info.kind = kHIThemeTabKindNormal; Info.adornment = kHIThemeTabPaneAdornmentNormal; OSStatus e = HIThemeDrawTabPane(&Bounds, &Info, pDC->Handle(), kHIThemeOrientationNormal); #else // Draw the inset area at 'd->Inset' LRect Bounds = d->Inset; pDC->Colour(d->cBorder); pDC->Box(&Bounds); Bounds.Inset(1, 1); pDC->Box(&Bounds); Bounds.Inset(1, 1); pDC->Colour(d->cFill); pDC->Rectangle(&Bounds); #endif int x = d->Tabs.x1, y = d->Tabs.y1; #ifndef LGI_CARBON LSurface *pScreen = pDC; #endif for (unsigned i = 0; i < it.Length(); i++) { auto Tab = it[i]; auto Foc = Focus(); LDisplayString *ds = Tab->d->GetDs(); bool First = i == 0; bool Last = i == it.Length() - 1; bool IsCurrent = d->Current == i; LRect r(0, 0, Tab->GetTabPx() - 1, d->Tabs.Y() - 1); r.Offset(x, y); #ifdef LGI_CARBON HIRect TabRc = r; HIThemeTabDrawInfo TabInfo; HIRect Label; TabInfo.version = 1; TabInfo.style = IsCurrent ? (Foc ? kThemeTabFront : kThemeTabNonFrontPressed) : kThemeTabNonFront; TabInfo.direction = Info.direction; TabInfo.size = Info.size; TabInfo.adornment = Info.adornment; TabInfo.kind = Info.kind; if (it.Length() == 1) TabInfo.position = kHIThemeTabPositionOnly; else if (First) TabInfo.position = kHIThemeTabPositionFirst; else if (Last) TabInfo.position = kHIThemeTabPositionLast; else TabInfo.position = kHIThemeTabPositionMiddle; e = HIThemeDrawTab(&TabRc, &TabInfo, pDC->Handle(), kHIThemeOrientationNormal, &Label); r = Label; #else LColour cTabFill = IsCurrent ? (Foc ? cFocusBack : d->cSelUnfoc) : LColour(L_WORKSPACE); LColour cInterTabBorder = d->Tint(0.9625); LRect b = r; #if MAC_DBL_BUF LMemDC Mem; if (First || Last) { if (Mem.Create(r.X(), r.Y(), System32BitColourSpace)) { pDC = &Mem; b.Offset(-b.x1, -b.y1); } Mem.Colour(LColour::Red); Mem.Rectangle(); } #endif pDC->Colour(d->cTopEdge); pDC->Line(b.x1, b.y1, b.x2, b.y1); // top edge if (i == 0) { pDC->Line(b.x1, b.y1, b.x1, b.y2); // left edge } else { pDC->Colour(cInterTabBorder); pDC->Line(b.x1, b.y1+1, b.x1, b.y2+1); // left edge } pDC->Colour(d->cBottomEdge); pDC->Line(b.x2, b.y2, b.x1, b.y2); // bottom edge if (Last) { pDC->Line(b.x2, b.y2, b.x2, b.y1); // right edge } else { pDC->Colour(cInterTabBorder); pDC->Line(b.x2, b.y2-1, b.x2, b.y1+1); // right edge between tabs } b.Inset(1, 1); pDC->Colour(cTabFill); pDC->Rectangle(&b); LRect Clip00(0, 0, MAC_STYLE_RADIUS-1, MAC_STYLE_RADIUS-1); auto Res = IsCurrent ? (Foc ? LTabViewPrivate::ResSelectedFocused : LTabViewPrivate::ResSelectedUnfocused) : LTabViewPrivate::ResWorkspace; if (First) { LRect Clip01 = Clip00.Move(0, r.Y() - Clip00.Y()); #if MAC_DBL_BUF // Erase the areas we will paint over pDC->Op(GDC_SET); pDC->Colour(pScreen->SupportsAlphaCompositing() ? LColour(0, 32) : NoPaint); pDC->Rectangle(&Clip00); pDC->Colour(pScreen->SupportsAlphaCompositing() ? LColour(0, 32) : d->cFill); pDC->Rectangle(&Clip01); #endif // Draw in the rounded corners pDC->Op(GDC_ALPHA); pDC->Blt(Clip00.x1, Clip00.y1, d->Corners[Res], Clip00); pDC->Blt(Clip01.x1, Clip01.y1, d->Corners[Res], Clip00.Move(0, MAC_STYLE_RADIUS)); } if (Last) { LRect Clip10 = Clip00.Move(r.X() - Clip00.X(), 0); LRect Clip11 = Clip00.Move(Clip10.x1, r.Y() - Clip00.Y()); #if MAC_DBL_BUF // Erase the areas we will paint over pDC->Op(GDC_SET); pDC->Colour(pScreen->SupportsAlphaCompositing() ? LColour(0, 32) : NoPaint); pDC->Rectangle(&Clip10); pDC->Colour(pScreen->SupportsAlphaCompositing() ? LColour(0, 32) : d->cFill); pDC->Rectangle(&Clip11); #endif // Draw in the rounded corners pDC->Op(GDC_ALPHA); pDC->Blt(Clip10.x1, Clip10.y1, d->Corners[Res], Clip00.Move(MAC_STYLE_RADIUS, 0)); pDC->Blt(Clip11.x1, Clip11.y1, d->Corners[Res], Clip00.Move(MAC_STYLE_RADIUS, MAC_STYLE_RADIUS)); } #if MAC_DBL_BUF if (First || Last) { if (pScreen->SupportsAlphaCompositing()) pScreen->Op(GDC_ALPHA); pScreen->Blt(r.x1, r.y1, &Mem); } #endif pDC = pScreen; #endif LFont *tf = ds->GetFont(); int BaselineOff = (int) (d->TabsBaseline - tf->Ascent()); tf->Transparent(true); LCss::ColorDef Fore; if (Tab->GetCss()) Fore = Tab->GetCss()->Color(); tf->Fore(Fore.IsValid() ? (LColour)Fore : IsCurrent && Foc ? cFocusFore : Tools.GetFore()); int DsX = r.x1 + TAB_MARGIN_X; int DsY = r.y1 + TAB_TXT_PAD + BaselineOff; ds->Draw(pDC, DsX, DsY, &r); if (Tab->HasButton()) { Tab->BtnPos.ZOff(CLOSE_BTN_SIZE-1, CLOSE_BTN_SIZE-1); Tab->BtnPos.Offset(DsX + ds->X() + CLOSE_BTN_GAP, r.y1 + ((r.Y()-Tab->BtnPos.Y()) >> 1)); Tab->OnButtonPaint(pDC); } else Tab->BtnPos.ZOff(-1, -1); it[i]->TabPos = r; x += r.X() #ifdef LGI_CARBON + (i ? -1 : 2) // Fudge factor to make it look nice, wtf apple? #endif ; } #if 0 pDC->Colour(LColour::Green); pDC->Line(0, 0, pDC->X()-1, pDC->Y()-1); #endif } else if (d->Style == TvWinXp) { if (LApp::SkinEngine && TestFlag(LApp::SkinEngine->GetFeatures(), GSKIN_TABVIEW)) { LSkinState State; State.pScreen = pDC; State.MouseOver = false; LApp::SkinEngine->OnPaint_LTabView(this, &State); } else { LRect r = GetTabClient(); r.Inset(-2, -2); LWideBorder(pDC, r, DefaultRaisedEdge); pDC->Colour(L_MED); pDC->Rectangle(0, 0, X()-1, d->TabClient.y1-3); LTabPage *Sel = 0; int x = r.x1; if (d->Scroll) { d->RightBtn.ZOff(12, TabY() - 2); x = d->RightBtn.x2 + 4; } else { d->RightBtn.ZOff(-1, -1); } d->LeftBtn.ZOff(-1, -1); for (int n=0; nScroll) { p->TabPos.ZOff(-1, -1); } else { int Wid = p->GetTabPx(); p->TabPos.ZOff(Wid, TabY()-3); p->TabPos.Offset(x, 2); if (p->TabPos.x2 > r.x2 - 16) { d->LeftBtn.x2 = X()-1; d->LeftBtn.x1 = d->LeftBtn.x2 - 12; d->LeftBtn.y1 = 0; d->LeftBtn.y2 = TabY() - 2; p->TabPos.ZOff(-1, -1); break; } if (d->Current != n) { p->PaintTab(pDC, false); } else { Sel = p; } x += Wid+1; } } if (!it.Length()) { pDC->Colour(L_MED); pDC->Rectangle(&r); } if (Sel) { Sel->PaintTab(pDC, true); } if (d->LeftBtn.Valid()) { r = d->LeftBtn; LWideBorder(pDC, r, DefaultRaisedEdge); int x = r.x1 + (r.X() >> 1) + 1; int y = r.y1 + (r.Y() >> 1) - 1; pDC->Colour(L_TEXT); for (int i=0; i<4; i++) { pDC->Line(x-i, y-i, x-i, y+i); } } if (d->RightBtn.Valid()) { r = d->RightBtn; LWideBorder(pDC, r, DefaultRaisedEdge); int x = r.x1 + (r.X() >> 1) - 2; int y = r.y1 + (r.Y() >> 1) - 1; pDC->Colour(L_TEXT); for (int i=0; i<4; i++) { pDC->Line(x+i, y-i, x+i, y+i); } } } } else LAssert(!"Not impl."); } void LTabView::OnPosChange() { GetTabClient(); if (Children.Length()) { TabIterator it(Children); LTabPage *p = it[d->Current]; if (p) { p->SetPos(d->TabClient, true); if (d->PourChildren) { LRect r = d->TabClient; r.Offset(-r.x1, -r.y1); LRegion Rgn(r); for (LViewI *c: p->IterateViews()) c->Pour(Rgn); } else { auto It = p->IterateViews(); if (It.Length() == 1) { LTableLayout *tl = dynamic_cast(It[0]); if (tl) { LRect r = p->GetClient(); r.Inset(LTableLayout::CellSpacing, LTableLayout::CellSpacing); tl->SetPos(r); } } } } } } ////////////////////////////////////////////////////////////////////////////////// char *_lgi_gview_cmp(LView *a, LView *b) { static char Str[256]; if (a && b) { #if !LGI_VIEW_HANDLE sprintf_s(Str, sizeof(Str), "LView: %p,%p", dynamic_cast(a), dynamic_cast(b)); #else sprintf_s(Str, sizeof(Str), "LView: %p,%p Hnd: %p,%p", dynamic_cast(a), dynamic_cast(b), (void*)a->Handle(), (void*)b->Handle()); #endif } else { Str[0] = 0; } return Str; } LTabPage::LTabPage(const char *name) : ResObject(Res_Tab) { d = new LTabPagePriv(this); LRect r(0, 0, 1000, 1000); SetPos(r); Name(name); Button = false; TabCtrl = 0; TabPos.ZOff(-1, -1); BtnPos.ZOff(-1, -1); GetCss(true)->Padding("4px"); #if WINNATIVE SetStyle(GetStyle() | WS_CLIPCHILDREN); CreateClassW32(GetClass(), 0, CS_HREDRAW | CS_VREDRAW); #elif defined(HAIKU) Visible(false); #endif LResources::StyleElement(this); } LTabPage::~LTabPage() { delete d; } int LTabPage::GetTabPx() { LDisplayString *Ds = d->GetDs(); int Px = TAB_MARGIN_X << 1; if (Ds) { Px += Ds->X(); if (Button) Px += CLOSE_BTN_GAP + CLOSE_BTN_SIZE; } return Px; } bool LTabPage::HasButton() { return Button; } void LTabPage::HasButton(bool b) { Button = b; if (GetParent()) GetParent()->Invalidate(); } void LTabPage::OnButtonClick(LMouse &m) { if (GetId() > 0) SendNotify(LNotification(LNotifyTabPageButtonClick, _FL)); } void LTabPage::OnTabClick(LMouse &m) { LViewI *v = GetId() > 0 ? this : GetParent(); v->SendNotify(LNotification(LNotifyItemClick, _FL)); } void LTabPage::OnButtonPaint(LSurface *pDC) { #if MAC_PAINT #else // The default is a close button LColour Low(L_LOW); LColour Mid(L_MED); Mid = Mid.Mix(Low); pDC->Colour(Mid); pDC->Line(BtnPos.x1, BtnPos.y1+1, BtnPos.x2-1, BtnPos.y2); pDC->Line(BtnPos.x1+1, BtnPos.y1, BtnPos.x2, BtnPos.y2-1); pDC->Line(BtnPos.x1, BtnPos.y2-1, BtnPos.x2-1, BtnPos.y1); pDC->Line(BtnPos.x1+1, BtnPos.y2, BtnPos.x2, BtnPos.y1+1); pDC->Colour(Low); pDC->Line(BtnPos.x1+1, BtnPos.y1+1, BtnPos.x2-1, BtnPos.y2-1); pDC->Line(BtnPos.x2-1, BtnPos.y1+1, BtnPos.x1+1, BtnPos.y2-1); #endif } int64 LTabPage::Value() { if (!TabCtrl) return false; ssize_t Idx = TabCtrl->IterateViews().IndexOf(this); return TabCtrl->Value() == Idx; } void LTabPage::Value(int64 v) { if (v) Select(); } const char *LTabPage::Name() { return LBase::Name(); } bool LTabPage::Name(const char *name) { bool Status = LView::Name(name); d->Ds.Reset(); if (GetParent()) GetParent()->Invalidate(); return Status; } void LTabPage::PaintTab(LSurface *pDC, bool Selected) { #if MAC_PAINT #else LRect r = TabPos; if (Selected) { r.Inset(-2, -2); } pDC->Colour(L_LIGHT); bool First = false; if (TabCtrl) { TabIterator it(TabCtrl->Children); First = it[0] == this; } if (First) pDC->Line(r.x1, r.y1+1, r.x1, r.y2); else pDC->Line(r.x1, r.y1+1, r.x1, r.y2-1); pDC->Line(r.x1+1, r.y1, r.x2-1, r.y1); pDC->Colour(L_HIGH); pDC->Line(r.x1+1, r.y1+1, r.x1+1, r.y2); pDC->Line(r.x1+1, r.y1+1, r.x2-1, r.y1+1); pDC->Colour(L_LOW); pDC->Line(r.x2-1, r.y1+1, r.x2-1, r.y2); pDC->Colour(L_SHADOW); pDC->Line(r.x2, r.y1+1, r.x2, r.y2-1); r.x2 -= 2; r.x1 += 2; r.y1 += 2; pDC->Colour(L_MED); pDC->Rectangle(&r); pDC->Set(r.x1, r.y1); pDC->Set(r.x2, r.y1); int Cx = r.x1 + TAB_MARGIN_X; auto t = Name(); if (t) { LFont *f = GetFont(); LDisplayString ds(f, t); f->Colour(L_TEXT, L_MED); f->Transparent(true); int y = r.y1 + ((r.Y()-ds.Y())/2); ds.Draw(pDC, Cx, y); if (TabCtrl->Focus() && Selected) { r.Set(Cx, y, Cx+ds.X(), y+ds.Y()); r.Inset(-2, -1); r.y1++; pDC->Colour(L_LOW); pDC->Box(&r); } Cx += ds.X() + CLOSE_BTN_GAP; } if (Button) { BtnPos.ZOff(CLOSE_BTN_SIZE-1, CLOSE_BTN_SIZE-1); BtnPos.Offset(Cx, r.y1 + ((r.Y()-BtnPos.Y()) / 2)); OnButtonPaint(pDC); } else BtnPos.ZOff(-1, -1); #endif } bool LTabPage::Attach(LViewI *parent) { bool Status = false; if (TabCtrl) { if (!IsAttached()) { Status = LView::Attach(parent); } else { Status = true; } for (auto w: Children) { if (!w->IsAttached()) { w->Attach(this); w->SetNotify(TabCtrl->GetParent()); } } } return Status; } LMessage::Result LTabPage::OnEvent(LMessage *Msg) { return LView::OnEvent(Msg); } void LTabPage::Append(LViewI *Wnd) { if (Wnd) { Wnd->SetNotify(TabCtrl); if (IsAttached() && TabCtrl) { Wnd->Attach(this); LTabView *v = dynamic_cast(GetParent()); if (v && v->GetPourChildren()) { v->OnPosChange(); } } else if (!HasView(Wnd)) { AddView(Wnd); } else LAssert(0); } } bool LTabPage::Remove(LViewI *Wnd) { if (Wnd) { LAssert(HasView(Wnd)); Wnd->Detach(); return true; } return false; } LColour LTabPage::GetBackground() { if (TabCtrl && TabCtrl->d->Style == TvMac) return LColour(226, 226, 226); // 207? else return LColour(L_MED); } void LTabPage::OnStyleChange() { SetFont(LSysFont, false); GetParent()->Invalidate(); } void LTabPage::SetFont(LFont *Font, bool OwnIt) { d->Ds.Reset(); Invalidate(); return LView::SetFont(Font, OwnIt); } void LTabPage::OnPaint(LSurface *pDC) { LRect r(0, 0, X()-1, Y()-1); LColour Bk = StyleColour(LCss::PropBackgroundColor, TabCtrl ? TabCtrl->d->cFill : LColour(L_MED), 1); pDC->Colour(Bk); pDC->Rectangle(&r); #if 0 pDC->Colour(LColour::Red); pDC->Line(0, 0, pDC->X()-1, pDC->Y()-1); #endif } void LTabPage::OnFocus(bool b) { } bool LTabPage::OnKey(LKey &k) { return false; } bool LTabPage::LoadFromResource(int Res) { LString n; auto ch = IterateViews(); LViewI *v; while ((v = ch[0])) { v->Detach(); DelView(v); ch.Delete(v, true); } bool Status = LResourceLoad::LoadFromResource(Res, this, 0, &n); if (ValidStr(n)) Name(n); /* This isn't needed if the controls properly inherit colours. if (TabCtrl && TabCtrl->d->Style == TvMac) // Sigh for (auto c : Children) c->GetCss(true)->BackgroundColor(LCss::ColorDef(GetBackground())); */ if (IsAttached()) AttachChildren(); return Status; } void LTabPage::Select() { if (GetParent()) { ssize_t Idx = GetParent()->IterateViews().IndexOf(this); if (Idx >= 0) GetParent()->Value(Idx); } }