openclonk/src/gui/C4GuiTabular.cpp

692 lines
23 KiB
C++

/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
*/
// generic user interface
// tab control
#include "C4Include.h"
#include "gui/C4Gui.h"
#include "graphics/C4Draw.h"
#include "graphics/C4FacetEx.h"
#include "graphics/C4GraphicsResource.h"
#include "gui/C4MouseControl.h"
namespace C4GUI
{
// ----------------------------------------------------
// Tabular::Sheet
Tabular::Sheet::Sheet(const char *szTitle, const C4Rect &rcBounds, int32_t icoTitle, bool fHasCloseButton, bool fTitleMarkup)
: Window(), icoTitle(icoTitle), cHotkey(0), dwCaptionClr(0u), fHasCloseButton(fHasCloseButton), fCloseButtonHighlighted(false), fTitleMarkup(fTitleMarkup)
{
// store title
if (szTitle)
{
sTitle.Copy(szTitle);
if (fTitleMarkup) ExpandHotkeyMarkup(sTitle, cHotkey);
}
// set bounds
SetBounds(rcBounds);
}
void Tabular::Sheet::DrawCaption(C4TargetFacet &cgo, int32_t x, int32_t y, int32_t iMaxWdt, bool fLarge, bool fActive, bool fFocus, C4Facet *pfctClip, C4Facet *pfctIcon, CStdFont *pUseFont)
{
// calculations
int32_t iTxtHgt, iTxtWdt;
GetCaptionSize(&iTxtWdt, &iTxtHgt, fLarge, fActive, pfctClip, pfctIcon, pUseFont);
if (pfctClip) iMaxWdt = iTxtWdt;
CStdFont &rUseFont = pUseFont ? *pUseFont : (fLarge ? ::GraphicsResource.CaptionFont : ::GraphicsResource.TextFont);
if (pfctClip && pfctIcon)
{
// tab with clip gfx: Icon on top of text
// x and y mark topleft pos; iTxtWdt and iTxtHgt mark overall size
pfctClip->Draw(cgo.Surface, x, y);
int32_t xCenter = x + iTxtWdt/2, yCenter = y + iTxtHgt/2;
int32_t iLabelHgt = rUseFont.GetLineHeight(); int32_t iIconLabelSpacing = 2;
int32_t yTop = yCenter - (pfctIcon->Hgt+iIconLabelSpacing+iLabelHgt)/2;
pfctIcon->Draw(cgo.Surface, xCenter-pfctIcon->Wdt/2, yTop, icoTitle);
pDraw->TextOut(sTitle.getData(), rUseFont, 1.0f, cgo.Surface, xCenter, yTop + pfctIcon->Hgt+iIconLabelSpacing, fActive ? C4GUI_GfxTabCaptActiveClr : C4GUI_GfxTabCaptInactiveClr , ACenter);
}
// focus highlight
if (fFocus)
{
pDraw->SetBlitMode(C4GFXBLIT_ADDITIVE);
::GraphicsResource.fctButtonHighlightRound.DrawX(cgo.Surface, (fLarge ? x : x - iTxtWdt/2)+5, y+3, (fLarge ? iMaxWdt : iTxtWdt)-10, iTxtHgt-6);
pDraw->ResetBlitMode();
}
if (!(pfctClip && pfctIcon))
{
// classical tab without clip
// icon
int32_t xo = x;
if (icoTitle>=0)
{
C4Facet cgoIcon(cgo.Surface, x, y+1, iTxtHgt-2, iTxtHgt-2);
if (fLarge)
{
// large caption: x parameter denotes left pos of icon
x += iTxtHgt + 2;
}
else
{
// small caption: x parameter denotes drawing center
// note that iTxtWdt includes the icon (and close button) as well
cgoIcon.X -= iTxtWdt / 2;
x += iTxtHgt / 2;
}
Icon::GetIconFacet((Icons)icoTitle).Draw(cgoIcon);
}
// text
if (!fLarge && fHasCloseButton) x -= iTxtHgt/2;
uint32_t dwClr = dwCaptionClr;
if (!dwClr) dwClr = fActive ? C4GUI_CaptionFontClr : C4GUI_InactCaptionFontClr;
pDraw->TextOut(sTitle.getData(), rUseFont, fLarge ? 1.2f : 1.0f, cgo.Surface, x, y, dwClr, fLarge ? ALeft : ACenter, fTitleMarkup);
// close button
if (fHasCloseButton)
{
xo += iTxtWdt / (2 - fLarge) - iTxtHgt + 1;
C4Facet cgoCloseBtn(cgo.Surface, xo, y+1, iTxtHgt-2, iTxtHgt-2);
if (!fCloseButtonHighlighted) pDraw->ActivateBlitModulation(0x7f7f7f);
Icon::GetIconFacet(Ico_Close).Draw(cgoCloseBtn);
if (!fCloseButtonHighlighted) pDraw->DeactivateBlitModulation();
}
}
}
void Tabular::Sheet::GetCaptionSize(int32_t *piWdt, int32_t *piHgt, bool fLarge, bool fActive, C4Facet *pfctClip, C4Facet *pfctIcon, CStdFont *pUseFont)
{
// caption by gfx?
if (pfctClip && pfctIcon)
{
if (piWdt) *piWdt = Tabular::GetLeftClipSize(pfctClip);
if (piHgt) *piHgt = pfctClip->Hgt;
return;
}
// caption by text
int32_t iWdt, iHgt;
CStdFont &rUseFont = pUseFont ? *pUseFont : (fLarge ? ::GraphicsResource.CaptionFont : ::GraphicsResource.TextFont);
if (!rUseFont.GetTextExtent(sTitle.getData(), iWdt, iHgt, fTitleMarkup))
{
iWdt=70; iHgt=rUseFont.GetLineHeight();
}
if (fLarge) { iWdt = iWdt * 6 / 5; iHgt = iHgt * 6 / 5; }
// add icon width
if (icoTitle>=0) iWdt += iHgt + fLarge*2;
// add close button width
if (fHasCloseButton) iWdt += iHgt + fLarge*2;
// assign output vars
if (piWdt) *piWdt = iWdt;
if (piHgt) *piHgt = iHgt;
}
bool Tabular::Sheet::IsPosOnCloseButton(int32_t x, int32_t y, int32_t iCaptWdt, int32_t iCaptHgt, bool fLarge)
{
// close button is on right end of tab
return fHasCloseButton && Inside<int32_t>(x, iCaptWdt-iCaptHgt+1, iCaptWdt-2) && Inside<int32_t>(y, 1, iCaptHgt-2);
}
void Tabular::Sheet::SetTitle(const char *szNewTitle)
{
if (sTitle == szNewTitle) return;
if (szNewTitle)
{
sTitle.Copy(szNewTitle);
if (fTitleMarkup) ExpandHotkeyMarkup(sTitle, cHotkey);
}
else
{
sTitle.Clear();
cHotkey = '\0';
}
Tabular *pTabular = static_cast<Tabular *>(GetParent());
if (pTabular) pTabular->SheetsChanged();
}
bool Tabular::Sheet::IsActiveSheet()
{
Tabular *pTabular = static_cast<Tabular *>(GetParent());
if (pTabular) return pTabular->GetActiveSheet() == this;
return false;
}
// ----------------------------------------------------
// Tabular
Tabular::Tabular(C4Rect &rtBounds, TabPosition eTabPos) : Control(rtBounds), pActiveSheet(nullptr), eTabPos(eTabPos), iMaxTabWidth(0),
iCaptionLengthTotal(0), iCaptionScrollPos(0), fScrollingLeft(false), fScrollingRight(false), fScrollingLeftDown(false),
fScrollingRightDown(false), iSheetMargin(4), fDrawSelf(true), pfctBack(nullptr), pfctClip(nullptr), pfctIcons(nullptr), pSheetCaptionFont(nullptr)
{
// calc client rect
UpdateOwnPos();
// key bindings for tab selection, if this is not an invisible "blind" tabular
if (eTabPos != tbNone)
{
// Ctrl+(Shift-)Tab works with dialog focus only (assumes max one tabular per dialog)
// Arrow keys work if control is focused only
C4CustomKey::CodeList Keys;
Keys.emplace_back(K_UP);
if (Config.Controls.GamepadGuiControl)
{
ControllerKeys::Up(Keys);
}
pKeySelUp = new C4KeyBinding(Keys, "GUITabularSelUp", KEYSCOPE_Gui,
new ControlKeyCB<Tabular>(*this, &Tabular::KeySelUp), C4CustomKey::PRIO_Ctrl);
Keys.clear();
Keys.emplace_back(K_DOWN);
if (Config.Controls.GamepadGuiControl)
{
ControllerKeys::Down(Keys);
}
pKeySelDown = new C4KeyBinding(Keys, "GUITabularSelDown", KEYSCOPE_Gui,
new ControlKeyCB<Tabular>(*this, &Tabular::KeySelDown), C4CustomKey::PRIO_Ctrl);
pKeySelUp2 = new C4KeyBinding(C4KeyCodeEx(K_TAB, C4KeyShiftState(KEYS_Shift | KEYS_Control)), "GUITabularSelUp2", KEYSCOPE_Gui,
new DlgKeyCB<Tabular>(*this, &Tabular::KeySelUp), C4CustomKey::PRIO_Ctrl);
pKeySelDown2 = new C4KeyBinding(C4KeyCodeEx(K_TAB, KEYS_Control), "GUITabularSelDown2", KEYSCOPE_Gui,
new DlgKeyCB<Tabular>(*this, &Tabular::KeySelDown), C4CustomKey::PRIO_Ctrl);
pKeyCloseTab = new C4KeyBinding(C4KeyCodeEx(K_F4, KEYS_Control), "GUITabularCloseTab", KEYSCOPE_Gui,
new DlgKeyCB<Tabular>(*this, &Tabular::KeyCloseTab), C4CustomKey::PRIO_Ctrl);
}
else
{
pKeySelUp = pKeySelDown = pKeySelUp2 = pKeySelDown2 = pKeyCloseTab = nullptr;
}
SheetsChanged();
}
Tabular::~Tabular()
{
if (pKeyCloseTab) delete pKeyCloseTab;
if (pKeySelDown2) delete pKeySelDown2;
if (pKeySelUp2) delete pKeySelUp2;
if (pKeySelDown) delete pKeySelDown;
if (pKeySelUp) delete pKeySelUp;
}
bool Tabular::KeySelUp()
{
// keyboard callback: Select previous sheet
int32_t iNewSel = GetActiveSheetIndex() - 1;
if (iNewSel < 0) iNewSel = GetSheetCount() - 1;
if (iNewSel < 0) return false;
SelectSheet(iNewSel, true);
return true;
}
bool Tabular::KeySelDown()
{
// keyboard callback: Select next sheet
int32_t iNewSel = GetActiveSheetIndex() + 1, iSheetCount = GetSheetCount();
if (iNewSel >= iSheetCount)
{
if (!iSheetCount) return false;
else iNewSel = 0;
}
SelectSheet(iNewSel, true);
return true;
}
bool Tabular::KeyCloseTab()
{
// keyboard callback: Close currnet sheet
// only for sheets that can be closed
Sheet *pCurrentSheet = GetActiveSheet();
if (!pCurrentSheet) return false;
if (!pCurrentSheet->HasCloseButton()) return false;
pCurrentSheet->UserClose();
return true;
}
void Tabular::SelectionChanged(bool fByUser)
{
Control *pFocusCtrl = nullptr;
Dialog *pDlg = GetDlg();
if (pDlg) pFocusCtrl = pDlg->GetFocus();
// any selection?
if (pActiveSheet)
{
// effect
if (fByUser) GUISound("UI::Select");
// update in sheet
pActiveSheet->OnShown(fByUser);
}
// make only active sheet visible
for (Element *pSheet = GetFirst(); pSheet; pSheet = pSheet->GetNext())
{
pSheet->SetVisibility(pSheet == pActiveSheet);
}
// if nothing is selected now, but something was selected before, focus new default control
if (pFocusCtrl && !pDlg->GetFocus()) pDlg->SetFocus(pDlg->GetDefaultControl(), fByUser);
}
void Tabular::SheetsChanged()
{
Sheet *pSheet;
if (eTabPos)
{
// update iMaxTabWidth by new set of sheet labels
iSheetOff = 20;
iMaxTabWidth = 20;
iSheetSpacing = (eTabPos == tbLeft) ? -10 : 20;
int32_t iSheetNum=0, iTotalHgt=iSheetOff;
iCaptionLengthTotal = iSheetOff;
for (pSheet = (Sheet *) GetFirst(); pSheet; pSheet = (Sheet *) pSheet->GetNext())
{
int32_t iTabWidth, iTabHeight;
pSheet->GetCaptionSize(&iTabWidth, &iTabHeight, HasLargeCaptions(), pSheet == pActiveSheet, pfctClip, pfctIcons, pSheetCaptionFont);
iTabWidth += (eTabPos == tbLeft) ? 20 : iSheetSpacing;
iMaxTabWidth = std::max(iTabWidth, iMaxTabWidth);
if (eTabPos == tbLeft)
{
iTotalHgt += iTabHeight;
if (iSheetNum++) iTotalHgt += iSheetSpacing;
}
else
{
iCaptionLengthTotal += iTabWidth;
}
}
// update sheet positioning
if (eTabPos == tbLeft && iTotalHgt > rcBounds.Hgt-GetMarginBottom())
{
// sheet captions dont fit - condense them
iSheetSpacing -= (iTotalHgt-rcBounds.Hgt+GetMarginBottom()-iSheetOff) / iSheetNum;
iSheetOff = 0;
}
else if (eTabPos == tbTop)
{
}
}
// update all sheet sizes
UpdateSize();
// update scrolling range/status
UpdateScrolling();
}
void Tabular::UpdateScrolling()
{
// any scrolling necessary?
int32_t iAvailableTabSpace = rcBounds.Wdt;
int32_t iScrollPinSize = GetTopSize();
if (eTabPos != tbTop || iCaptionLengthTotal <= iAvailableTabSpace || iAvailableTabSpace <= iScrollPinSize*2)
{
fScrollingLeft = fScrollingRight = fScrollingLeftDown = fScrollingRightDown = false;
iCaptionScrollPos = 0;
}
else
{
// must scroll; update scrolling parameters
fScrollingLeft = !!iCaptionScrollPos;
if (!fScrollingLeft)
{
fScrollingRight = true;
fScrollingLeftDown = false;
}
else
{
iAvailableTabSpace -= iScrollPinSize;
fScrollingRight = (iCaptionLengthTotal - iCaptionScrollPos > iAvailableTabSpace);
// do not scroll past right end
if (!fScrollingRight)
{
iCaptionScrollPos = iCaptionLengthTotal - iAvailableTabSpace;
fScrollingRightDown = false;
}
}
}
}
void Tabular::DoCaptionScroll(int32_t iDir)
{
// store time of scrolling change
tLastScrollTime = C4TimeMilliseconds::Now();
// change scrolling within max range
int32_t iAvailableTabSpace = rcBounds.Wdt;
int32_t iScrollPinSize = GetTopSize();
iCaptionScrollPos = Clamp<int32_t>(iCaptionScrollPos + iDir*iAvailableTabSpace/2, 0, iCaptionLengthTotal - iAvailableTabSpace + iScrollPinSize);
UpdateScrolling();
}
void Tabular::DrawElement(C4TargetFacet &cgo)
{
if (!fDrawSelf) return;
bool fGfx = HasGfx();
// execute scrolling
bool fCaptionScrollDelayOver = C4TimeMilliseconds::Now() - tLastScrollTime >= C4GUI_TabCaptionScrollTime;
if ((fScrollingLeftDown || fScrollingRightDown) && fCaptionScrollDelayOver)
DoCaptionScroll(fScrollingRightDown - fScrollingLeftDown);
// border
if (!fGfx) Draw3DFrame(cgo, false, 1, 0xaf, eTabPos!=tbTop, GetTopSize(), eTabPos!=tbLeft, GetLeftSize());
// calc positions
int32_t x0 = cgo.TargetX + rcBounds.x + GetLeftSize(),
y0 = cgo.TargetY + rcBounds.y + GetTopSize(),
x1 = cgo.TargetX + rcBounds.x + rcBounds.Wdt - 1,
y1 = cgo.TargetY + rcBounds.y + rcBounds.Hgt - 1;
// main area BG
if (!fGfx) pDraw->DrawBoxDw(cgo.Surface, x0,y0,x1,y1, C4GUI_StandardBGColor);
// no tabs?
if (!eTabPos)
{
if (fGfx)
pfctBack->DrawX(cgo.Surface, x0, y0, x1-x0+1, y1-y0+1);
return;
}
bool fLeft = (eTabPos == tbLeft);
// top or left bar
int32_t d=(fLeft ? y0 : x0)+iSheetOff; // current tab position (leave some space to the left/top)
int32_t ad0=0,ad1=0, aCptTxX=0, aCptTxY=0;
// scrolling in captions
int32_t iScrollSize = GetTopSize();
if (fScrollingLeft) d -= iCaptionScrollPos + iScrollSize;
// tabs
for (Sheet *pSheet = (Sheet *) GetFirst(); pSheet; pSheet = (Sheet *) pSheet->GetNext())
{
// get tab size
int32_t iTabWidth, iTabHeight;
pSheet->GetCaptionSize(&iTabWidth, &iTabHeight, HasLargeCaptions(), pSheet == pActiveSheet, pfctClip, pfctIcons, pSheetCaptionFont);
// leave some space around caption
iTabWidth += fLeft ? 20 : iSheetSpacing;
iTabHeight += fLeft ? iSheetSpacing : 10;
// draw caption bg
if (!fGfx)
{
float vtx[8];
if (fLeft)
{
vtx[0] = x0; vtx[1] = d;
vtx[2] = x0-GetLeftSize(); vtx[3] = d;
vtx[4] = x0-GetLeftSize(); vtx[5] = d+iTabHeight;
vtx[6] = x0; vtx[7] = d+iTabHeight;
}
else
{
vtx[0] = d+1; vtx[1] = y0;
vtx[2] = d+4+1; vtx[3] = y0-GetTopSize();
vtx[4] = d+iTabWidth-4; vtx[5] = y0-GetTopSize();
vtx[6] = d+iTabWidth; vtx[7] = y0;
}
DWORD dwClr = (pSheet == pActiveSheet) ? C4GUI_ActiveTabBGColor : C4GUI_StandardBGColor;
pDraw->DrawQuadDw(cgo.Surface, vtx, dwClr, dwClr, dwClr, dwClr, nullptr);
// draw caption frame
// TODO: Switch to PerformMultiLines
pDraw->DrawLineDw(cgo.Surface, (float)vtx[0]-1 , (float)vtx[1] , (float)vtx[2]-1 ,(float)vtx[3] , C4GUI_BorderColorA1);
pDraw->DrawLineDw(cgo.Surface, (float)vtx[2]-1 , (float)vtx[3] , (float)vtx[4]-fLeft,(float)vtx[5] , C4GUI_BorderColorA1);
pDraw->DrawLineDw(cgo.Surface, (float)vtx[4] , (float)vtx[5] , (float)vtx[6] ,(float)vtx[7] , C4GUI_BorderColorA1);
pDraw->DrawLineDw(cgo.Surface, (float)vtx[0] , (float)vtx[1]+fLeft, (float)vtx[2] ,(float)vtx[3]+fLeft , C4GUI_BorderColorA2);
pDraw->DrawLineDw(cgo.Surface, (float)vtx[2]-!fLeft, (float)vtx[3]+1 , (float)vtx[4] ,(float)vtx[5]+!fLeft , C4GUI_BorderColorA2);
pDraw->DrawLineDw(cgo.Surface, (float)vtx[4]+1 , (float)vtx[5]+fLeft, (float)vtx[6] ,(float)vtx[7]+fLeft , C4GUI_BorderColorA2);
}
// draw caption text
int32_t iCptTextX = fLeft ? (x0-GetLeftSize()+10) : (d+iTabWidth/2);
int32_t iCptTextY = fLeft ? (d+iSheetSpacing/2) : (y0-GetTopSize()+2);
if (pSheet == pActiveSheet)
{
// store active sheet pos for border line or later drawing
ad0=d; ad1=d+(fLeft ? iTabHeight : iTabWidth);
aCptTxX = iCptTextX; aCptTxY = iCptTextY;
// draw active caption
if (!fGfx) pSheet->DrawCaption(cgo, iCptTextX, iCptTextY, iMaxTabWidth, fLeft, true, HasDrawFocus(), nullptr, nullptr, nullptr);
}
else
{
// draw inactive caption
pSheet->DrawCaption(cgo, iCptTextX, iCptTextY, iMaxTabWidth, fLeft, false, false, pfctClip, pfctIcons, pSheetCaptionFont);
}
// advance position
d += (fLeft ? iTabHeight : iTabWidth)+2;
}
// draw tab border line across everything but active tab
if (!fGfx) if (ad0||ad1)
{
pDraw->DrawLineDw(cgo.Surface, (float)x0 ,(float)y0 ,(float)(fLeft ? x0 : ad0), (float)(fLeft ? ad0 : y0), C4GUI_BorderColorA1);
pDraw->DrawLineDw(cgo.Surface, (float)(x0+1),(float)(y0+1),(float)((fLeft ? x0 : ad0)+1), (float)((fLeft ? ad0 : y0)+1) , C4GUI_BorderColorA2);
pDraw->DrawLineDw(cgo.Surface, (float)(fLeft ? x0 : ad1), (float)(fLeft ? ad1 : y0), (float)(fLeft ? x0 : x1), (float)(fLeft ? y1 : y0), C4GUI_BorderColorA1);
pDraw->DrawLineDw(cgo.Surface, (float)((fLeft ? x0 : ad1)+1), (float)((fLeft ? ad1 : y0)+1), (float)((fLeft ? x0 : x1)+1), (float)((fLeft ? y1 : y0)+1), C4GUI_BorderColorA2);
}
// main area bg in gfx: Atop inactive tabs
if (fGfx)
{
pfctBack->DrawX(cgo.Surface, x0, y0, x1-x0+1, y1-y0+1);
// and active tab on top of that
if (pActiveSheet)
pActiveSheet->DrawCaption(cgo, aCptTxX, aCptTxY, iMaxTabWidth, fLeft, true, HasDrawFocus(), pfctClip, pfctIcons, pSheetCaptionFont);
}
// scrolling
if (fScrollingLeft) ::GraphicsResource.fctBigArrows.DrawX(cgo.Surface, x0+iSheetOff,y0-iScrollSize, iScrollSize,iScrollSize, fScrollingLeftDown*2);
if (fScrollingRight) ::GraphicsResource.fctBigArrows.DrawX(cgo.Surface, x1-iScrollSize,y0-iScrollSize, iScrollSize,iScrollSize, 1+fScrollingRightDown*2);
}
void Tabular::MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
{
// tabular contains controls?
if (eTabPos)
{
bool fLeft = (eTabPos == tbLeft);
bool fInCaptionArea = ((!fLeft && Inside<int32_t>(iY, 0, GetTopSize())) || (fLeft && Inside<int32_t>(iX, 0, GetLeftSize())));
if (!fInCaptionArea || iButton == C4MC_Button_LeftUp)
{
MouseLeaveCaptionArea();
}
// then check for mousedown in coloumn area
else if ((iButton == C4MC_Button_LeftDown || iButton == C4MC_Button_None) && fInCaptionArea)
{
int32_t d=iSheetOff;
// check inside scrolling buttons
bool fProcessed = false;
if (fScrollingLeft || fScrollingRight)
{
int32_t iScrollSize = GetTopSize();
if (iButton == C4MC_Button_LeftDown && fScrollingRight && Inside(iX, rcBounds.Wdt-iScrollSize,rcBounds.Wdt))
{
fProcessed = fScrollingRightDown = true;
GUISound("UI::Select");
DoCaptionScroll(+1);
}
else if (fScrollingLeft)
{
if (iButton == C4MC_Button_LeftDown && Inside(iX, d,d+iScrollSize))
{
fProcessed = fScrollingLeftDown = true;
GUISound("UI::Select");
DoCaptionScroll(-1);
}
d -= iCaptionScrollPos + iScrollSize;
}
}
// check on sheet captions
if (!fProcessed) for (Sheet *pSheet = (Sheet *) GetFirst(); pSheet; pSheet = (Sheet *) pSheet->GetNext())
{
// default: Mouse not on close button
pSheet->SetCloseButtonHighlight(false);
// get tab width
int32_t iCaptWidth,iCaptHeight,iTabWidth,iTabHeight;
pSheet->GetCaptionSize(&iCaptWidth, &iCaptHeight, HasLargeCaptions(), pSheet == pActiveSheet, pfctClip, pfctIcons, pSheetCaptionFont);
iTabWidth = iCaptWidth + (fLeft ? 20 : iSheetSpacing);
iTabHeight = iCaptHeight + (fLeft ? iSheetSpacing : 10);
// check containment in this tab (check rect only, may catch some side-clicks...)
if ((!fLeft && Inside(iX, d, d+iTabWidth)) || (fLeft && Inside(iY, d, d+iTabHeight)))
{
// close button
if (pSheet->IsPosOnCloseButton(iX-d*!fLeft-(iTabWidth-iCaptWidth)/2, iY-d*fLeft, iCaptWidth, iCaptHeight, HasLargeCaptions()))
{
if (iButton == C4MC_Button_LeftDown)
{
// Closing: Callback to sheet
pSheet->UserClose();
}
else
// just moving :Highlight
pSheet->SetCloseButtonHighlight(true);
}
// mouse press outside close button area: Switch sheet
else if (iButton == C4MC_Button_LeftDown)
{
if (pSheet != pActiveSheet)
{
pActiveSheet = pSheet;
SelectionChanged(true);
}
}
break;
}
// next tab
d += (fLeft ? iTabHeight : iTabWidth)+2;
}
}
}
// inherited
Control::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
}
void Tabular::MouseLeaveCaptionArea()
{
// no more close buttons or scroll buttons highlighted
for (Sheet *pSheet = (Sheet *) GetFirst(); pSheet; pSheet = (Sheet *) pSheet->GetNext())
{
// default: Mouse not on close button
pSheet->SetCloseButtonHighlight(false);
}
if (fScrollingLeftDown || fScrollingRightDown)
{
// stop scrolling
GUISound("UI::Select");
fScrollingLeftDown = fScrollingRightDown = false;
}
}
void Tabular::MouseLeave(CMouse &rMouse)
{
MouseLeaveCaptionArea();
// inherited
Control::MouseLeave(rMouse);
}
void Tabular::OnGetFocus(bool fByMouse)
{
// inherited (tooltip)
Control::OnGetFocus(fByMouse);
}
void Tabular::RemoveElement(Element *pChild)
{
// inherited
Control::RemoveElement(pChild);
// clear selection var
if (pChild == pActiveSheet)
{
// select new active sheet
pActiveSheet = (Sheet *) GetFirst();
SelectionChanged(false);
}
// update sheet labels
SheetsChanged();
}
Tabular::Sheet *Tabular::AddSheet(const char *szTitle, int32_t icoTitle)
{
// create new sheet in client area
Sheet *pNewSheet = new Sheet(szTitle, GetContainedClientRect(), icoTitle);
AddCustomSheet(pNewSheet);
// k, new sheet ready!
return pNewSheet;
}
void Tabular::AddCustomSheet(Sheet *pAddSheet)
{
AddElement(pAddSheet);
// select it if it's first
pAddSheet->SetVisibility(!pActiveSheet);
if (!pActiveSheet) pActiveSheet = pAddSheet;
// update sheet labels
SheetsChanged();
}
void Tabular::ClearSheets()
{
// del all sheets
Sheet *pSheet;
while ((pSheet = GetSheet(0))) delete pSheet;
SheetsChanged();
}
void Tabular::SelectSheet(int32_t iIndex, bool fByUser)
{
pActiveSheet = GetSheet(iIndex);
SelectionChanged(fByUser);
}
void Tabular::SelectSheet(Sheet *pSelSheet, bool fByUser)
{
pActiveSheet = pSelSheet;
SelectionChanged(fByUser);
}
int32_t Tabular::GetActiveSheetIndex()
{
int32_t i=-1;
Sheet *pSheet;
while ((pSheet = GetSheet(++i))) if (pSheet == pActiveSheet) return i;
return -1;
}
void Tabular::SetGfx(C4Facet *pafctBack, C4Facet *pafctClip, C4Facet *pafctIcons, CStdFont *paSheetCaptionFont, bool fResizeByAspect)
{
// set gfx files
pfctBack=pafctBack;
pfctClip=pafctClip;
pfctIcons=pafctIcons;
pSheetCaptionFont=paSheetCaptionFont;
// make sure aspect of background is used correctly
if (pfctBack && fResizeByAspect)
{
int32_t iEffWdt = rcBounds.Wdt - GetLeftSize(), iEffHgt = rcBounds.Hgt - GetTopSize();
if (iEffWdt * pfctBack->Hgt > pfctBack->Wdt * iEffHgt)
{
// control is too wide: center it
int32_t iOversize = iEffWdt - pfctBack->Wdt * iEffHgt / pfctBack->Hgt;
C4Rect rtBounds = GetBounds();
rtBounds.x += iOversize/2;
rtBounds.Wdt -= iOversize;
SetBounds(rtBounds);
}
else
{
// control is too tall: cap at bottom
int32_t iOversize = iEffHgt - pfctBack->Hgt * iEffWdt / pfctBack->Wdt;
C4Rect rtBounds = GetBounds();
rtBounds.y += iOversize;
rtBounds.Hgt -= iOversize;
SetBounds(rtBounds);
}
}
SheetsChanged();
}
void Tabular::UpdateSize()
{
Control::UpdateSize();
// update all sheets
for (Sheet *pSheet = static_cast<Sheet *>(GetFirst()); pSheet; pSheet = static_cast<Sheet *>(pSheet->GetNext()))
pSheet->SetBounds(GetContainedClientRect());
}
} // end of namespace