pcsx2/common/pxStaticText.cpp

392 lines
9.2 KiB
C++

/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <wx/wizard.h>
#include "common/pxStaticText.h"
#include "common/Assertions.h"
// --------------------------------------------------------------------------------------
// pxStaticText (implementations)
// --------------------------------------------------------------------------------------
pxStaticText::pxStaticText(wxWindow* parent)
: _parent(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER)
{
m_align = wxALIGN_CENTRE_HORIZONTAL;
m_autowrap = true;
m_wrappedWidth = -1;
m_heightInLines = 1;
SetPaddingDefaults();
}
pxStaticText::pxStaticText(wxWindow* parent, const wxString& label, wxAlignment align)
: _parent(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER)
{
m_heightInLines = 1;
m_align = align;
SetPaddingDefaults();
Init(label);
}
void pxStaticText::Init(const wxString& label)
{
m_autowrap = true;
m_wrappedWidth = -1;
m_label = label;
Bind(wxEVT_PAINT, &pxStaticText::paintEvent, this);
}
// we need to refresh the window after changing its size as the standard
// control doesn't always update itself properly (fials typically on window resizes where
// the control is expanded to fit -- ie the control's size changes but the position does not)
void pxStaticText::DoSetSize(int x, int y, int w, int h, int sizeFlags)
{
_parent::DoSetSize(x, y, w, h, sizeFlags);
Refresh();
}
void pxStaticText::SetPaddingDefaults()
{
m_paddingPix_horiz = 7;
m_paddingPix_vert = 1;
m_paddingPct_horiz = 0.0f;
m_paddingPct_vert = 0.0f;
}
pxStaticText& pxStaticText::SetMinWidth(int width)
{
SetMinSize(wxSize(width, GetMinHeight()));
return *this;
}
pxStaticText& pxStaticText::SetMinHeight(int height)
{
SetMinSize(wxSize(GetMinWidth(), height));
return *this;
}
pxStaticText& pxStaticText::SetHeight(int lines)
{
if (!pxAssert(lines > 0))
lines = 2;
m_heightInLines = lines;
const int newHeight = (pxGetCharHeight(this) * m_heightInLines) + (m_paddingPix_vert * 2);
SetMinSize(wxSize(GetMinWidth(), newHeight));
return *this;
}
pxStaticText& pxStaticText::Align(wxAlignment align)
{
m_align = align;
return *this;
}
pxStaticText& pxStaticText::Bold()
{
wxFont bold(GetFont());
bold.SetWeight(wxFONTWEIGHT_BOLD);
SetFont(bold);
return *this;
}
pxStaticText& pxStaticText::PaddingPixH(int pixels)
{
m_paddingPix_horiz = pixels;
UpdateWrapping(false);
Refresh();
return *this;
}
pxStaticText& pxStaticText::PaddingPixV(int pixels)
{
m_paddingPix_vert = pixels;
Refresh();
return *this;
}
pxStaticText& pxStaticText::PaddingPctH(float pct)
{
pxAssert(pct < 0.5);
m_paddingPct_horiz = pct;
UpdateWrapping(false);
Refresh();
return *this;
}
pxStaticText& pxStaticText::PaddingPctV(float pct)
{
pxAssert(pct < 0.5);
m_paddingPct_vert = pct;
Refresh();
return *this;
}
pxStaticText& pxStaticText::Unwrapped()
{
m_autowrap = false;
UpdateWrapping(false);
return *this;
}
int pxStaticText::calcPaddingWidth(int newWidth) const
{
return (int)(newWidth * m_paddingPct_horiz * 2) + (m_paddingPix_horiz * 2);
}
int pxStaticText::calcPaddingHeight(int newHeight) const
{
return (int)(newHeight * m_paddingPct_vert * 2) + (m_paddingPix_vert * 2);
}
wxSize pxStaticText::GetBestWrappedSize(const wxClientDC& dc) const
{
pxAssert(m_autowrap);
// Find an ideal(-ish) width, based on a search of all parent controls and their
// valid Minimum sizes.
int idealWidth = wxDefaultCoord;
int parentalAdjust = 0;
double parentalFactor = 1.0;
const wxWindow* millrun = this;
while (millrun)
{
// IMPORTANT : wxWizard changes its min size and then expects everything else
// to play nice and NOT resize according to the new min size. (wtf stupid)
// Anyway, this fixes it -- ignore min size specifier on wxWizard!
if (wxIsKindOf(millrun, wxWizard))
break;
int min = (int)((millrun->GetMinWidth() - parentalAdjust) * parentalFactor);
if (min > 0 && ((idealWidth < 0) || (min < idealWidth)))
{
idealWidth = min;
}
parentalAdjust += pxSizerFlags::StdPadding * 2;
millrun = millrun->GetParent();
}
if (idealWidth <= 0)
{
// FIXME: The minimum size of this control is unknown, so let's just pick a guess based on
// the size of the user's display area.
idealWidth = (int)(wxGetDisplaySize().GetWidth() * 0.66) - (parentalAdjust * 2);
}
return dc.GetMultiLineTextExtent(pxTextWrapper().Wrap(this, m_label, idealWidth - calcPaddingWidth(idealWidth)).GetResult());
}
pxStaticText& pxStaticText::WrapAt(int width)
{
m_autowrap = false;
if ((width <= 1) || (width == m_wrappedWidth))
return *this;
wxString wrappedLabel;
m_wrappedWidth = width;
if (width > 1)
wrappedLabel = pxTextWrapper().Wrap(this, m_label, width).GetResult();
if (m_wrappedLabel != wrappedLabel)
{
m_wrappedLabel = wrappedLabel;
wxSize area = wxClientDC(this).GetMultiLineTextExtent(m_wrappedLabel);
SetMinSize(wxSize(
area.GetWidth() + calcPaddingWidth(area.GetWidth()),
area.GetHeight() + calcPaddingHeight(area.GetHeight())));
}
return *this;
}
bool pxStaticText::_updateWrapping(bool textChanged)
{
if (!m_autowrap)
{
//m_wrappedLabel = wxEmptyString;
//m_wrappedWidth = -1;
return false;
}
wxString wrappedLabel;
int newWidth = GetSize().GetWidth();
if (!textChanged && (newWidth == m_wrappedWidth))
return false;
// Note: during various stages of sizer-calc, width can be 1, 0, or -1.
// We ignore wrapping in these cases. (the PaintEvent also checks the wrapping
// and updates it if needed, in case the control's size isn't figured out prior
// to being painted).
m_wrappedWidth = newWidth;
if (m_wrappedWidth > 1)
wrappedLabel = pxTextWrapper().Wrap(this, m_label, m_wrappedWidth).GetResult();
if (m_wrappedLabel == wrappedLabel)
return false;
m_wrappedLabel = wrappedLabel;
return true;
}
void pxStaticText::UpdateWrapping(bool textChanged)
{
if (_updateWrapping(textChanged))
Refresh();
}
void pxStaticText::SetLabel(const wxString& label)
{
const bool labelChanged(label != m_label);
if (labelChanged)
{
m_label = label;
Refresh();
}
// Always update wrapping, in case window width or something else also changed.
UpdateWrapping(labelChanged);
InvalidateBestSize();
}
wxFont pxStaticText::GetFontOk() const
{
wxFont font(GetFont());
if (!font.Ok())
return wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
return font;
}
bool pxStaticText::Enable(bool enabled)
{
if (_parent::Enable(enabled))
{
Refresh();
return true;
}
return false;
}
void pxStaticText::paintEvent(wxPaintEvent& evt)
{
wxPaintDC dc(this);
const int dcWidth = dc.GetSize().GetWidth();
const int dcHeight = dc.GetSize().GetHeight();
if (dcWidth < 1)
return;
dc.SetFont(GetFontOk());
if (IsEnabled())
dc.SetTextForeground(GetForegroundColour());
else
dc.SetTextForeground(*wxLIGHT_GREY);
pxWindowTextWriter writer(dc);
writer.Align(m_align);
const wxString& label(m_autowrap ? m_wrappedLabel : m_label);
if (m_autowrap)
_updateWrapping(false);
int tWidth, tHeight;
dc.GetMultiLineTextExtent(label, &tWidth, &tHeight);
writer.Align(m_align);
if (m_align & wxALIGN_CENTER_VERTICAL)
writer.SetY((dcHeight - tHeight) / 2);
else
writer.SetY((int)(dcHeight * m_paddingPct_vert) + m_paddingPix_vert);
writer.WriteLn(label); // without formatting please.
//dc.SetBrush( *wxTRANSPARENT_BRUSH );
//dc.DrawRectangle(wxPoint(), dc.GetSize());
}
// Overloaded form wxPanel and friends.
wxSize pxStaticText::DoGetBestSize() const
{
wxClientDC dc(const_cast<pxStaticText*>(this));
dc.SetFont(GetFontOk());
wxSize best;
if (m_autowrap)
{
best = GetBestWrappedSize(dc);
//best.x = wxDefaultCoord;
}
else
{
// No autowrapping, so we can force a specific size here!
best = dc.GetMultiLineTextExtent(GetLabel());
best.x += calcPaddingWidth(best.x);
}
best.y += calcPaddingHeight(best.y);
CacheBestSize(best);
return best;
}
// --------------------------------------------------------------------------------------
// pxStaticHeading (implementations)
// --------------------------------------------------------------------------------------
pxStaticHeading::pxStaticHeading(wxWindow* parent, const wxString& label)
: _parent(parent)
{
m_align = wxALIGN_CENTER;
SetPaddingDefaults();
Init(label);
}
void pxStaticHeading::SetPaddingDefaults()
{
m_paddingPix_horiz = 4;
m_paddingPix_vert = 1;
m_paddingPct_horiz = 0.08f;
m_paddingPct_vert = 0.0f;
}
void operator+=(wxSizer& target, pxStaticText* src)
{
if (src)
target.Add(src, pxExpand);
}
void operator+=(wxSizer& target, pxStaticText& src)
{
target.Add(&src, pxExpand);
}
void operator+=(wxSizer* target, pxStaticText& src)
{
target->Add(&src, pxExpand);
}