mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 10:13:19 +02:00
Testing on platforms shows we don't need this differentiated case between toolbar and non toolbar icons. Since we don't start with an small base bundle icon for the icons that probably had issues before.
453 lines
12 KiB
C++
453 lines
12 KiB
C++
/*
|
|
* This program source code file is part of KICAD, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2020 Ian McInerney <ian.s.mcinerney at ieee dot org>
|
|
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program 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 this program; if not, you may find one here:
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <kiplatform/ui.h>
|
|
#include <pgm_base.h>
|
|
#include <settings/common_settings.h>
|
|
#include <widgets/bitmap_button.h>
|
|
#include <wx/button.h>
|
|
#include <wx/dcclient.h>
|
|
#include <wx/renderer.h>
|
|
#include <wx/settings.h>
|
|
|
|
#define wxCONTROL_SEPARATOR wxCONTROL_SPECIAL
|
|
|
|
|
|
BITMAP_BUTTON::BITMAP_BUTTON( wxWindow* aParent, wxWindowID aId, const wxPoint& aPos,
|
|
const wxSize& aSize, int aStyles ) :
|
|
wxPanel( aParent, aId, aPos, aSize, aStyles ),
|
|
m_isRadioButton( false ),
|
|
m_showBadge( false ),
|
|
m_badgeColor( wxColor( 210, 0, 0 ) ), // dark red
|
|
m_badgeTextColor( wxColor( wxT( "white" ) ) ),
|
|
m_buttonState( 0 ),
|
|
m_padding( 0 ),
|
|
m_isToolbarButton( false ),
|
|
m_acceptDraggedInClicks( false ),
|
|
m_centerBitmap( true )
|
|
{
|
|
m_badgeFont = GetFont().Smaller().MakeBold();
|
|
|
|
setupEvents();
|
|
}
|
|
|
|
|
|
BITMAP_BUTTON::BITMAP_BUTTON( wxWindow* aParent, wxWindowID aId, const wxBitmap& aDummyBitmap,
|
|
const wxPoint& aPos, const wxSize& aSize, int aStyles ) :
|
|
wxPanel( aParent, aId, aPos, aSize, aStyles ),
|
|
m_isRadioButton( false ),
|
|
m_showBadge( false ),
|
|
m_badgeColor( wxColor( 210, 0, 0 ) ), // dark red
|
|
m_badgeTextColor( wxColor( wxT( "white" ) ) ),
|
|
m_buttonState( 0 ),
|
|
m_padding( 5 ),
|
|
m_isToolbarButton( false ),
|
|
m_acceptDraggedInClicks( false ),
|
|
m_centerBitmap( true )
|
|
{
|
|
m_badgeFont = GetFont().Smaller().MakeBold();
|
|
|
|
setupEvents();
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::setupEvents()
|
|
{
|
|
Bind( wxEVT_DPI_CHANGED, &BITMAP_BUTTON::OnDPIChanged, this );
|
|
Bind( wxEVT_PAINT, &BITMAP_BUTTON::OnPaint, this );
|
|
Bind( wxEVT_LEFT_UP, &BITMAP_BUTTON::OnLeftButtonUp, this );
|
|
Bind( wxEVT_LEFT_DOWN, &BITMAP_BUTTON::OnLeftButtonDown, this );
|
|
Bind( wxEVT_LEAVE_WINDOW, &BITMAP_BUTTON::OnMouseLeave, this );
|
|
Bind( wxEVT_ENTER_WINDOW, &BITMAP_BUTTON::OnMouseEnter, this );
|
|
Bind( wxEVT_KILL_FOCUS, &BITMAP_BUTTON::OnKillFocus, this );
|
|
Bind( wxEVT_SET_FOCUS, &BITMAP_BUTTON::OnSetFocus, this );
|
|
}
|
|
|
|
|
|
BITMAP_BUTTON::~BITMAP_BUTTON()
|
|
{
|
|
Unbind( wxEVT_DPI_CHANGED, &BITMAP_BUTTON::OnDPIChanged, this );
|
|
Unbind( wxEVT_PAINT, &BITMAP_BUTTON::OnPaint, this );
|
|
Unbind( wxEVT_LEFT_UP, &BITMAP_BUTTON::OnLeftButtonUp, this );
|
|
Unbind( wxEVT_LEFT_DOWN, &BITMAP_BUTTON::OnLeftButtonDown, this );
|
|
Unbind( wxEVT_LEAVE_WINDOW, &BITMAP_BUTTON::OnMouseLeave, this );
|
|
Unbind( wxEVT_ENTER_WINDOW, &BITMAP_BUTTON::OnMouseEnter, this );
|
|
Unbind( wxEVT_KILL_FOCUS, &BITMAP_BUTTON::OnKillFocus, this );
|
|
Unbind( wxEVT_SET_FOCUS, &BITMAP_BUTTON::OnSetFocus, this );
|
|
}
|
|
|
|
|
|
wxSize BITMAP_BUTTON::DoGetBestSize() const
|
|
{
|
|
if( hasFlag( wxCONTROL_SEPARATOR ) )
|
|
return wxSize( m_unadjustedMinSize.x + m_padding * 2, wxButton::GetDefaultSize().y );
|
|
|
|
return m_unadjustedMinSize + wxSize( m_padding * 2, m_padding * 2 );
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::invalidateBestSize()
|
|
{
|
|
// Uncomment to override wxFB sizes
|
|
// SetMinSize( DoGetBestSize() );
|
|
|
|
InvalidateBestSize();
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::SetPadding( int aPadding )
|
|
{
|
|
m_padding = aPadding;
|
|
|
|
invalidateBestSize();
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::SetBitmap( const wxBitmapBundle& aBmp )
|
|
{
|
|
m_normalBitmap = aBmp;
|
|
|
|
// This is a bit of a hack, but fixes button scaling issues on some platforms when those buttons
|
|
// use KiScaledBitmap. When that method is retired, this can probably be revisited.
|
|
if( m_isToolbarButton )
|
|
{
|
|
m_unadjustedMinSize = m_normalBitmap.GetPreferredBitmapSizeFor( this );
|
|
}
|
|
else
|
|
{
|
|
#ifndef __WXMSW__
|
|
m_unadjustedMinSize = m_normalBitmap.GetDefaultSize();
|
|
#else
|
|
m_unadjustedMinSize = m_normalBitmap.GetPreferredBitmapSizeFor( this );
|
|
#endif
|
|
}
|
|
|
|
invalidateBestSize();
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::SetDisabledBitmap( const wxBitmapBundle& aBmp )
|
|
{
|
|
m_disabledBitmap = aBmp;
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::AcceptDragInAsClick( bool aAcceptDragIn )
|
|
{
|
|
m_acceptDraggedInClicks = aAcceptDragIn;
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::OnMouseLeave( wxEvent& aEvent )
|
|
{
|
|
if( hasFlag( wxCONTROL_CURRENT | wxCONTROL_PRESSED ) )
|
|
{
|
|
clearFlag( wxCONTROL_CURRENT | wxCONTROL_PRESSED );
|
|
Refresh();
|
|
}
|
|
|
|
aEvent.Skip();
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::OnMouseEnter( wxEvent& aEvent )
|
|
{
|
|
if( !hasFlag( wxCONTROL_CURRENT ) )
|
|
{
|
|
setFlag( wxCONTROL_CURRENT );
|
|
Refresh();
|
|
}
|
|
|
|
aEvent.Skip();
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::OnKillFocus( wxEvent& aEvent )
|
|
{
|
|
if( hasFlag( wxCONTROL_FOCUSED | wxCONTROL_CURRENT | wxCONTROL_PRESSED | wxCONTROL_SELECTED ) )
|
|
{
|
|
clearFlag( wxCONTROL_FOCUSED | wxCONTROL_CURRENT | wxCONTROL_PRESSED | wxCONTROL_SELECTED );
|
|
Refresh();
|
|
}
|
|
|
|
aEvent.Skip();
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::OnSetFocus( wxEvent& aEvent )
|
|
{
|
|
if( !hasFlag( wxCONTROL_CHECKABLE ) )
|
|
{
|
|
if( !hasFlag( wxCONTROL_FOCUSED ) )
|
|
{
|
|
setFlag( wxCONTROL_FOCUSED );
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
aEvent.Skip();
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::OnLeftButtonUp( wxMouseEvent& aEvent )
|
|
{
|
|
// Only create a button event when the control is enabled
|
|
// and only accept clicks that came without prior mouse-down if configured
|
|
if( !hasFlag( wxCONTROL_DISABLED )
|
|
&& ( m_acceptDraggedInClicks || hasFlag( wxCONTROL_PRESSED | wxCONTROL_FOCUSED ) ) )
|
|
{
|
|
GetEventHandler()->CallAfter( [this]()
|
|
{
|
|
wxCommandEvent evt( wxEVT_BUTTON, GetId() );
|
|
evt.SetEventObject( this );
|
|
GetEventHandler()->ProcessEvent( evt );
|
|
} );
|
|
}
|
|
|
|
clearFlag( wxCONTROL_PRESSED );
|
|
Refresh();
|
|
|
|
aEvent.Skip();
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::OnLeftButtonDown( wxMouseEvent& aEvent )
|
|
{
|
|
if( hasFlag( wxCONTROL_CHECKABLE ) )
|
|
{
|
|
if( hasFlag( wxCONTROL_CHECKED ) && !m_isRadioButton )
|
|
{
|
|
clearFlag( wxCONTROL_CHECKED );
|
|
|
|
GetEventHandler()->CallAfter(
|
|
[this]()
|
|
{
|
|
wxCommandEvent evt( wxEVT_BUTTON, GetId() );
|
|
evt.SetEventObject( this );
|
|
evt.SetInt( 0 );
|
|
GetEventHandler()->ProcessEvent( evt );
|
|
} );
|
|
}
|
|
else
|
|
{
|
|
setFlag( wxCONTROL_CHECKED );
|
|
|
|
GetEventHandler()->CallAfter(
|
|
[this]()
|
|
{
|
|
wxCommandEvent evt( wxEVT_BUTTON, GetId() );
|
|
evt.SetEventObject( this );
|
|
evt.SetInt( 1 );
|
|
GetEventHandler()->ProcessEvent( evt );
|
|
} );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
setFlag( wxCONTROL_PRESSED );
|
|
}
|
|
|
|
Refresh();
|
|
|
|
aEvent.Skip();
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::OnDPIChanged( wxDPIChangedEvent& aEvent )
|
|
{
|
|
wxSize newBmSize = m_normalBitmap.GetPreferredBitmapSizeFor( this );
|
|
|
|
if( newBmSize != m_unadjustedMinSize )
|
|
{
|
|
m_unadjustedMinSize = newBmSize;
|
|
invalidateBestSize();
|
|
}
|
|
|
|
aEvent.Skip();
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::OnPaint( wxPaintEvent& aEvent )
|
|
{
|
|
bool darkMode = KIPLATFORM::UI::IsDarkTheme();
|
|
wxColor highlightColor = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT );
|
|
|
|
// The drawing rectangle
|
|
wxRect rect( wxPoint( 0, 0 ), GetSize() );
|
|
wxPaintDC dc( this );
|
|
|
|
if( hasFlag( wxCONTROL_SEPARATOR ) )
|
|
{
|
|
dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ) );
|
|
dc.DrawLine( wxPoint( GetSize().x / 2, 0 ), wxPoint( GetSize().x / 2, GetSize().y ) );
|
|
return;
|
|
}
|
|
|
|
// This drawing is done so the button looks the same as an AUI toolbar button
|
|
if( !hasFlag( wxCONTROL_DISABLED ) )
|
|
{
|
|
if( hasFlag( wxCONTROL_PRESSED ) )
|
|
{
|
|
dc.SetPen( wxPen( highlightColor ) );
|
|
dc.SetBrush( wxBrush( highlightColor.ChangeLightness( darkMode ? 20 : 150 ) ) );
|
|
dc.DrawRectangle( rect );
|
|
}
|
|
else if( hasFlag( wxCONTROL_CURRENT | wxCONTROL_FOCUSED ) )
|
|
{
|
|
dc.SetPen( wxPen( highlightColor ) );
|
|
dc.SetBrush( wxBrush( highlightColor.ChangeLightness( darkMode ? 40 : 170 ) ) );
|
|
|
|
// Checked items need a lighter hover rectangle
|
|
if( hasFlag( wxCONTROL_CHECKED ) )
|
|
dc.SetBrush( wxBrush( highlightColor.ChangeLightness( darkMode ? 50 : 180 ) ) );
|
|
|
|
dc.DrawRectangle( rect );
|
|
}
|
|
else if( hasFlag( wxCONTROL_CHECKED ) )
|
|
{
|
|
dc.SetPen( wxPen( highlightColor ) );
|
|
dc.SetBrush( wxBrush( highlightColor.ChangeLightness( darkMode ? 40 : 170 ) ) );
|
|
dc.DrawRectangle( rect );
|
|
}
|
|
}
|
|
|
|
const wxBitmapBundle& bmp = hasFlag( wxCONTROL_DISABLED ) ? m_disabledBitmap : m_normalBitmap;
|
|
|
|
wxPoint drawBmpPos( m_padding, m_padding );
|
|
wxBitmap bmpImg;
|
|
double scale = KIPLATFORM::UI::GetContentScaleFactor( this );
|
|
wxSize bmSize;
|
|
|
|
if( bmp.IsOk() )
|
|
{
|
|
bmpImg = bmp.GetBitmap( ToPhys( m_unadjustedMinSize ) );
|
|
bmSize = bmpImg.GetLogicalSize();
|
|
}
|
|
|
|
if( m_centerBitmap )
|
|
{
|
|
drawBmpPos.x = ( rect.width - bmSize.x ) / 2;
|
|
drawBmpPos.y = ( rect.height - bmSize.y ) / 2;
|
|
}
|
|
|
|
// Draw the bitmap with the upper-left corner offset by the padding
|
|
if( bmp.IsOk() )
|
|
dc.DrawBitmap( bmpImg, drawBmpPos, true );
|
|
|
|
// Draw the badge
|
|
if( m_showBadge )
|
|
{
|
|
dc.SetFont( m_badgeFont );
|
|
|
|
wxSize text_padding( 3, 1 );
|
|
|
|
if( m_padding )
|
|
text_padding *= 2;
|
|
|
|
wxSize box_size = dc.GetTextExtent( m_badgeText ) + text_padding;
|
|
wxSize box_offset = box_size;
|
|
|
|
if( m_padding != 0 )
|
|
box_offset += wxSize( m_padding / 3, m_padding / 3 );
|
|
|
|
dc.SetPen( wxPen( m_badgeColor ) );
|
|
dc.SetBrush( wxBrush( m_badgeColor ) );
|
|
dc.DrawRoundedRectangle( rect.GetRightBottom() - box_offset, box_size, -0.25 );
|
|
|
|
dc.SetTextForeground( m_badgeTextColor );
|
|
dc.DrawText( m_badgeText, rect.GetRightBottom() - box_offset + ( text_padding / 2 ) );
|
|
}
|
|
}
|
|
|
|
|
|
bool BITMAP_BUTTON::Enable( bool aEnable )
|
|
{
|
|
// If the requested state is already the current state, don't do anything
|
|
if( aEnable != hasFlag( wxCONTROL_DISABLED ) )
|
|
return false;
|
|
|
|
wxPanel::Enable( aEnable );
|
|
|
|
if( aEnable && hasFlag( wxCONTROL_DISABLED ) )
|
|
{
|
|
clearFlag( wxCONTROL_DISABLED );
|
|
Refresh();
|
|
}
|
|
|
|
if( !aEnable && !hasFlag( wxCONTROL_DISABLED ) )
|
|
{
|
|
setFlag( wxCONTROL_DISABLED );
|
|
Refresh();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::SetIsCheckButton()
|
|
{
|
|
setFlag( wxCONTROL_CHECKABLE );
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::SetIsRadioButton()
|
|
{
|
|
setFlag( wxCONTROL_CHECKABLE );
|
|
m_isRadioButton = true;
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::SetIsSeparator()
|
|
{
|
|
setFlag( wxCONTROL_SEPARATOR | wxCONTROL_DISABLED );
|
|
|
|
invalidateBestSize();
|
|
}
|
|
|
|
|
|
void BITMAP_BUTTON::Check( bool aCheck )
|
|
{
|
|
wxASSERT_MSG( hasFlag( wxCONTROL_CHECKABLE ), wxS( "Button is not a checkButton." ) );
|
|
|
|
if( aCheck && !hasFlag( wxCONTROL_CHECKED ) )
|
|
{
|
|
setFlag( wxCONTROL_CHECKED );
|
|
Refresh();
|
|
}
|
|
|
|
if( !aCheck && hasFlag( wxCONTROL_CHECKED ) )
|
|
{
|
|
clearFlag( wxCONTROL_CHECKED );
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
|
|
bool BITMAP_BUTTON::IsChecked() const
|
|
{
|
|
wxASSERT_MSG( hasFlag( wxCONTROL_CHECKABLE ), wxS( "Button is not a checkButton." ) );
|
|
|
|
return hasFlag( wxCONTROL_CHECKED );
|
|
}
|