Ensure that the entire banner window uses the same background colour as the bitmap, even when the bitmap is too small for the window size. This allows to avoid using huge bitmaps if extending the bitmap with solid colour looks good enough. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@69859 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
345 lines
8.9 KiB
C++
345 lines
8.9 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: wx/bannerwindow.h
|
|
// Purpose: wxBannerWindow class implementation
|
|
// Author: Vadim Zeitlin
|
|
// Created: 2011-08-16
|
|
// RCS-ID: $Id$
|
|
// Copyright: (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
|
|
// Licence: wxWindows licence
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_BANNERWINDOW
|
|
|
|
#include "wx/bannerwindow.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/bitmap.h"
|
|
#include "wx/colour.h"
|
|
#endif
|
|
|
|
#include "wx/dcbuffer.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
// Some constants for banner layout, currently they're hard coded but we could
|
|
// easily make them configurable if needed later.
|
|
const int MARGIN_X = 5;
|
|
const int MARGIN_Y = 5;
|
|
|
|
} // anonymous namespace
|
|
|
|
const char wxBannerWindowNameStr[] = "bannerwindow";
|
|
|
|
BEGIN_EVENT_TABLE(wxBannerWindow, wxWindow)
|
|
EVT_SIZE(wxBannerWindow::OnSize)
|
|
EVT_PAINT(wxBannerWindow::OnPaint)
|
|
END_EVENT_TABLE()
|
|
|
|
void wxBannerWindow::Init()
|
|
{
|
|
m_direction = wxLEFT;
|
|
|
|
m_colStart = *wxWHITE;
|
|
m_colEnd = *wxBLUE;
|
|
}
|
|
|
|
bool
|
|
wxBannerWindow::Create(wxWindow* parent,
|
|
wxWindowID winid,
|
|
wxDirection dir,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxString& name)
|
|
{
|
|
if ( !wxWindow::Create(parent, winid, pos, size, style, name) )
|
|
return false;
|
|
|
|
wxASSERT_MSG
|
|
(
|
|
dir == wxLEFT || dir == wxRIGHT || dir == wxTOP || dir == wxBOTTOM,
|
|
wxS("Invalid banner direction")
|
|
);
|
|
|
|
m_direction = dir;
|
|
|
|
SetBackgroundStyle(wxBG_STYLE_PAINT);
|
|
|
|
return true;
|
|
}
|
|
|
|
void wxBannerWindow::SetBitmap(const wxBitmap& bmp)
|
|
{
|
|
m_bitmap = bmp;
|
|
|
|
m_colBitmapBg = wxColour();
|
|
|
|
InvalidateBestSize();
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void wxBannerWindow::SetText(const wxString& title, const wxString& message)
|
|
{
|
|
m_title = title;
|
|
m_message = message;
|
|
|
|
InvalidateBestSize();
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void wxBannerWindow::SetGradient(const wxColour& start, const wxColour& end)
|
|
{
|
|
m_colStart = start;
|
|
m_colEnd = end;
|
|
|
|
Refresh();
|
|
}
|
|
|
|
wxFont wxBannerWindow::GetTitleFont() const
|
|
{
|
|
wxFont font = GetFont();
|
|
font.MakeBold().MakeLarger();
|
|
return font;
|
|
}
|
|
|
|
wxSize wxBannerWindow::DoGetBestClientSize() const
|
|
{
|
|
if ( m_bitmap.IsOk() )
|
|
{
|
|
return m_bitmap.GetSize();
|
|
}
|
|
else
|
|
{
|
|
wxClientDC dc(const_cast<wxBannerWindow *>(this));
|
|
const wxSize sizeText = dc.GetMultiLineTextExtent(m_message);
|
|
|
|
dc.SetFont(GetTitleFont());
|
|
|
|
const wxSize sizeTitle = dc.GetTextExtent(m_title);
|
|
|
|
wxSize sizeWin(wxMax(sizeTitle.x, sizeText.x), sizeTitle.y + sizeText.y);
|
|
|
|
// If we draw the text vertically width and height are swapped.
|
|
if ( m_direction == wxLEFT || m_direction == wxRIGHT )
|
|
wxSwap(sizeWin.x, sizeWin.y);
|
|
|
|
sizeWin += 2*wxSize(MARGIN_X, MARGIN_Y);
|
|
|
|
return sizeWin;
|
|
}
|
|
}
|
|
|
|
void wxBannerWindow::OnSize(wxSizeEvent& event)
|
|
{
|
|
Refresh();
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void wxBannerWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
|
|
{
|
|
if ( m_bitmap.IsOk() && m_title.empty() && m_message.empty() )
|
|
{
|
|
// No need for buffering in this case.
|
|
wxPaintDC dc(this);
|
|
|
|
DrawBitmapBackground(dc);
|
|
}
|
|
else // We need to compose our contents ourselves.
|
|
{
|
|
wxAutoBufferedPaintDC dc(this);
|
|
|
|
// Deal with the background first.
|
|
if ( m_bitmap.IsOk() )
|
|
{
|
|
DrawBitmapBackground(dc);
|
|
}
|
|
else // Draw gradient background.
|
|
{
|
|
wxDirection gradientDir;
|
|
if ( m_direction == wxLEFT )
|
|
{
|
|
gradientDir = wxTOP;
|
|
}
|
|
else if ( m_direction == wxRIGHT )
|
|
{
|
|
gradientDir = wxBOTTOM;
|
|
}
|
|
else // For both wxTOP and wxBOTTOM.
|
|
{
|
|
gradientDir = wxRIGHT;
|
|
}
|
|
|
|
dc.GradientFillLinear(GetClientRect(), m_colStart, m_colEnd,
|
|
gradientDir);
|
|
}
|
|
|
|
// Now draw the text on top of it.
|
|
dc.SetFont(GetTitleFont());
|
|
|
|
wxPoint pos(MARGIN_X, MARGIN_Y);
|
|
DrawBannerTextLine(dc, m_title, pos);
|
|
pos.y += dc.GetTextExtent(m_title).y;
|
|
|
|
dc.SetFont(GetFont());
|
|
|
|
wxArrayString lines = wxSplit(m_message, '\n', '\0');
|
|
const unsigned numLines = lines.size();
|
|
for ( unsigned n = 0; n < numLines; n++ )
|
|
{
|
|
const wxString& line = lines[n];
|
|
|
|
DrawBannerTextLine(dc, line, pos);
|
|
pos.y += dc.GetTextExtent(line).y;
|
|
}
|
|
}
|
|
}
|
|
|
|
wxColour wxBannerWindow::GetBitmapBg()
|
|
{
|
|
if ( m_colBitmapBg.IsOk() )
|
|
return m_colBitmapBg;
|
|
|
|
// Determine the colour to use to extend the bitmap. It's the colour of the
|
|
// bitmap pixels at the edge closest to the area where it can be extended.
|
|
wxImage image(m_bitmap.ConvertToImage());
|
|
|
|
// The point we get the colour from. The choice is arbitrary and in general
|
|
// the bitmap should have the same colour on the entire edge of this point
|
|
// for extending it to look good.
|
|
wxPoint p;
|
|
|
|
wxSize size = image.GetSize();
|
|
size.x--;
|
|
size.y--;
|
|
|
|
switch ( m_direction )
|
|
{
|
|
case wxTOP:
|
|
case wxBOTTOM:
|
|
// The bitmap will be extended to the right.
|
|
p.x = size.x;
|
|
p.y = 0;
|
|
break;
|
|
|
|
case wxLEFT:
|
|
// The bitmap will be extended from the top.
|
|
p.x = 0;
|
|
p.y = 0;
|
|
break;
|
|
|
|
case wxRIGHT:
|
|
// The bitmap will be extended to the bottom.
|
|
p.x = 0;
|
|
p.y = size.y;
|
|
break;
|
|
|
|
// This case is there only to prevent g++ warnings about not handling
|
|
// some enum elements in the switch, it can't really happen.
|
|
case wxALL:
|
|
wxFAIL_MSG( wxS("Unreachable") );
|
|
}
|
|
|
|
m_colBitmapBg.Set(image.GetRed(p.x, p.y),
|
|
image.GetGreen(p.x, p.y),
|
|
image.GetBlue(p.x, p.y));
|
|
|
|
return m_colBitmapBg;
|
|
}
|
|
|
|
void wxBannerWindow::DrawBitmapBackground(wxDC& dc)
|
|
{
|
|
// We may need to fill the part of the background not covered by the bitmap
|
|
// with the solid colour extending the bitmap, this rectangle will hold the
|
|
// area to be filled (which could be empty if the bitmap is big enough).
|
|
wxRect rectSolid;
|
|
|
|
const wxSize size = GetClientSize();
|
|
|
|
switch ( m_direction )
|
|
{
|
|
case wxTOP:
|
|
case wxBOTTOM:
|
|
// Draw the bitmap at the origin, its rightmost could be truncated,
|
|
// as it's meant to be.
|
|
dc.DrawBitmap(m_bitmap, 0, 0);
|
|
|
|
rectSolid.x = m_bitmap.GetWidth();
|
|
rectSolid.width = size.x - rectSolid.x;
|
|
rectSolid.height = size.y;
|
|
break;
|
|
|
|
case wxLEFT:
|
|
// The top most part of the bitmap may be truncated but its bottom
|
|
// must be always visible so intentionally draw it possibly partly
|
|
// outside of the window.
|
|
rectSolid.width = size.x;
|
|
rectSolid.height = size.y - m_bitmap.GetHeight();
|
|
dc.DrawBitmap(m_bitmap, 0, rectSolid.height);
|
|
break;
|
|
|
|
case wxRIGHT:
|
|
// Draw the bitmap at the origin, possibly truncating its
|
|
// bottommost part.
|
|
dc.DrawBitmap(m_bitmap, 0, 0);
|
|
|
|
rectSolid.y = m_bitmap.GetHeight();
|
|
rectSolid.height = size.y - rectSolid.y;
|
|
rectSolid.width = size.x;
|
|
break;
|
|
|
|
// This case is there only to prevent g++ warnings about not handling
|
|
// some enum elements in the switch, it can't really happen.
|
|
case wxALL:
|
|
wxFAIL_MSG( wxS("Unreachable") );
|
|
}
|
|
|
|
if ( rectSolid.width > 0 && rectSolid.height > 0 )
|
|
{
|
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
|
dc.SetBrush(GetBitmapBg());
|
|
dc.DrawRectangle(rectSolid);
|
|
}
|
|
}
|
|
|
|
void
|
|
wxBannerWindow::DrawBannerTextLine(wxDC& dc,
|
|
const wxString& str,
|
|
const wxPoint& pos)
|
|
{
|
|
switch ( m_direction )
|
|
{
|
|
case wxTOP:
|
|
case wxBOTTOM:
|
|
// The simple case: we just draw the text normally.
|
|
dc.DrawText(str, pos);
|
|
break;
|
|
|
|
case wxLEFT:
|
|
// We draw the text vertically and start from the lower left
|
|
// corner and not the upper left one as usual.
|
|
dc.DrawRotatedText(str, pos.y, GetClientSize().y - pos.x, 90);
|
|
break;
|
|
|
|
case wxRIGHT:
|
|
// We also draw the text vertically but now we start from the upper
|
|
// right corner and draw it from top to bottom.
|
|
dc.DrawRotatedText(str, GetClientSize().x - pos.y, pos.x, -90);
|
|
break;
|
|
|
|
case wxALL:
|
|
wxFAIL_MSG( wxS("Unreachable") );
|
|
}
|
|
}
|
|
|
|
#endif // wxUSE_BANNERWINDOW
|