Files
wxWidgets/src/generic/tipwin.cpp
Maarten Bent 10ac26d6e5 Fix DPI of wxTipWindow
Take care to use the DPI of the display the window will be actually
displayed on and not that of its parent window, which can be different.
2020-07-11 19:59:40 +02:00

348 lines
9.6 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/generic/tipwin.cpp
// Purpose: implementation of wxTipWindow
// Author: Vadim Zeitlin
// Modified by:
// Created: 10.09.00
// Copyright: (c) 2000 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_TIPWINDOW
#include "wx/tipwin.h"
#ifndef WX_PRECOMP
#include "wx/dcclient.h"
#include "wx/timer.h"
#include "wx/settings.h"
#endif // WX_PRECOMP
#include "wx/display.h"
#include "wx/vector.h"
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
static const wxCoord TEXT_MARGIN_X = 3;
static const wxCoord TEXT_MARGIN_Y = 3;
// ----------------------------------------------------------------------------
// wxTipWindowView
// ----------------------------------------------------------------------------
// Viewer window to put in the frame
class WXDLLEXPORT wxTipWindowView : public wxWindow
{
public:
wxTipWindowView(wxWindow *parent);
// event handlers
void OnPaint(wxPaintEvent& event);
void OnMouseClick(wxMouseEvent& event);
void OnMouseMove(wxMouseEvent& event);
// calculate the client rect we need to display the text
void Adjust(const wxString& text, wxCoord maxLength);
private:
wxTipWindow* m_parent;
wxVector<wxString> m_textLines;
wxCoord m_heightLine;
wxDECLARE_EVENT_TABLE();
wxDECLARE_NO_COPY_CLASS(wxTipWindowView);
};
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// event tables
// ----------------------------------------------------------------------------
wxBEGIN_EVENT_TABLE(wxTipWindow, wxPopupTransientWindow)
EVT_LEFT_DOWN(wxTipWindow::OnMouseClick)
EVT_RIGHT_DOWN(wxTipWindow::OnMouseClick)
EVT_MIDDLE_DOWN(wxTipWindow::OnMouseClick)
wxEND_EVENT_TABLE()
wxBEGIN_EVENT_TABLE(wxTipWindowView, wxWindow)
EVT_PAINT(wxTipWindowView::OnPaint)
EVT_LEFT_DOWN(wxTipWindowView::OnMouseClick)
EVT_RIGHT_DOWN(wxTipWindowView::OnMouseClick)
EVT_MIDDLE_DOWN(wxTipWindowView::OnMouseClick)
EVT_MOTION(wxTipWindowView::OnMouseMove)
wxEND_EVENT_TABLE()
// ----------------------------------------------------------------------------
// wxTipWindow
// ----------------------------------------------------------------------------
wxTipWindow::wxTipWindow(wxWindow *parent,
const wxString& text,
wxCoord maxLength,
wxTipWindow** windowPtr,
wxRect *rectBounds)
: wxPopupTransientWindow(parent)
{
SetTipWindowPtr(windowPtr);
if ( rectBounds )
{
SetBoundingRect(*rectBounds);
}
// set colours
SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOTEXT));
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK));
int x, y;
wxGetMousePosition(&x, &y);
// move to the center of the target display so wxTipWindowView will use the
// correct DPI
wxPoint posScreen;
wxSize sizeScreen;
const int displayNum = wxDisplay::GetFromPoint(wxPoint(x, y));
if ( displayNum != wxNOT_FOUND )
{
const wxRect rectScreen = wxDisplay(displayNum).GetGeometry();
posScreen = rectScreen.GetPosition();
sizeScreen = rectScreen.GetSize();
}
else // outside of any display?
{
// just use the primary one then
posScreen = wxPoint(0, 0);
sizeScreen = wxGetDisplaySize();
}
wxPoint center(posScreen.x + sizeScreen.GetWidth() / 2,
posScreen.y + sizeScreen.GetHeight() / 2);
Move(center, wxSIZE_NO_ADJUSTMENTS);
// set size, position and show it
m_view = new wxTipWindowView(this);
m_view->Adjust(text, FromDIP(parent->ToDIP(maxLength)) );
// we want to show the tip below the mouse, not over it, make sure to not
// overflow into the next display
//
// NB: the reason we use "/ 2" here is that we don't know where the current
// cursors hot spot is... it would be nice if we could find this out
// though
int cursorOffset = wxSystemSettings::GetMetric(wxSYS_CURSOR_Y, this) / 2;
if (y + cursorOffset >= posScreen.y + sizeScreen.GetHeight())
cursorOffset = posScreen.y + sizeScreen.GetHeight() - y - 1;
y += cursorOffset;
Position(wxPoint(x, y), wxSize(0,0));
Popup(m_view);
#ifdef __WXGTK__
m_view->CaptureMouse();
#endif
}
wxTipWindow::~wxTipWindow()
{
if ( m_windowPtr )
{
*m_windowPtr = NULL;
}
#ifdef __WXGTK__
if ( m_view->HasCapture() )
m_view->ReleaseMouse();
#endif
}
void wxTipWindow::OnMouseClick(wxMouseEvent& WXUNUSED(event))
{
Close();
}
void wxTipWindow::OnDismiss()
{
Close();
}
void wxTipWindow::SetBoundingRect(const wxRect& rectBound)
{
m_rectBound = rectBound;
}
void wxTipWindow::Close()
{
if ( m_windowPtr )
{
*m_windowPtr = NULL;
m_windowPtr = NULL;
}
Show(false);
#ifdef __WXGTK__
if ( m_view->HasCapture() )
m_view->ReleaseMouse();
#endif
// Under OS X we get destroyed because of wxEVT_KILL_FOCUS generated by
// Show(false).
#ifndef __WXOSX__
Destroy();
#endif
}
// ----------------------------------------------------------------------------
// wxTipWindowView
// ----------------------------------------------------------------------------
wxTipWindowView::wxTipWindowView(wxWindow *parent)
: wxWindow(parent, wxID_ANY,
wxDefaultPosition, wxDefaultSize,
wxNO_BORDER)
{
// set colours
SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOTEXT));
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK));
m_parent = (wxTipWindow*)parent;
m_heightLine = 0;
}
void wxTipWindowView::Adjust(const wxString& text, wxCoord maxLength)
{
wxClientDC dc(this);
dc.SetFont(GetFont());
// calculate the length: we want each line be no longer than maxLength
// pixels and we only break lines at words boundary
wxString current;
wxCoord height, width,
widthMax = 0;
bool breakLine = false;
for ( const wxChar *p = text.c_str(); ; p++ )
{
if ( *p == wxT('\n') || *p == wxT('\0') )
{
dc.GetTextExtent(current, &width, &height);
if ( width > widthMax )
widthMax = width;
if ( height > m_heightLine )
m_heightLine = height;
m_textLines.push_back(current);
if ( !*p )
{
// end of text
break;
}
current.clear();
breakLine = false;
}
else if ( breakLine && (*p == wxT(' ') || *p == wxT('\t')) )
{
// word boundary - break the line here
m_textLines.push_back(current);
current.clear();
breakLine = false;
}
else // line goes on
{
current += *p;
dc.GetTextExtent(current, &width, &height);
if ( width > maxLength )
breakLine = true;
if ( width > widthMax )
widthMax = width;
if ( height > m_heightLine )
m_heightLine = height;
}
}
// take into account the border size and the margins
width = 2*(TEXT_MARGIN_X + 1) + widthMax;
height = 2*(TEXT_MARGIN_Y + 1) + wx_truncate_cast(wxCoord, m_textLines.size())*m_heightLine;
m_parent->SetClientSize(width, height);
SetSize(0, 0, width, height);
}
void wxTipWindowView::OnPaint(wxPaintEvent& WXUNUSED(event))
{
wxPaintDC dc(this);
wxRect rect;
wxSize size = GetClientSize();
rect.width = size.x;
rect.height = size.y;
// first filll the background
dc.SetBrush(wxBrush(GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
dc.SetPen(wxPen(GetForegroundColour(), 1, wxPENSTYLE_SOLID));
dc.DrawRectangle(rect);
// and then draw the text line by line
dc.SetTextBackground(GetBackgroundColour());
dc.SetTextForeground(GetForegroundColour());
dc.SetFont(GetFont());
wxPoint pt;
pt.x = TEXT_MARGIN_X;
pt.y = TEXT_MARGIN_Y;
const size_t count = m_textLines.size();
for ( size_t n = 0; n < count; n++ )
{
dc.DrawText(m_textLines[n], pt);
pt.y += m_heightLine;
}
}
void wxTipWindowView::OnMouseClick(wxMouseEvent& WXUNUSED(event))
{
m_parent->Close();
}
void wxTipWindowView::OnMouseMove(wxMouseEvent& event)
{
const wxRect& rectBound = m_parent->m_rectBound;
if ( rectBound.width &&
!rectBound.Contains(ClientToScreen(event.GetPosition())) )
{
// mouse left the bounding rect, disappear
m_parent->Close();
}
else
{
event.Skip();
}
}
#endif // wxUSE_TIPWINDOW