2020-01-03 15:09:26 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KICAD, a free EDA CAD application.
|
|
|
|
*
|
2021-02-09 16:35:43 +00:00
|
|
|
* Copyright (C) 2016 Anil8735(https://stackoverflow.com/users/3659387/anil8753)
|
|
|
|
* from https://stackoverflow.com/a/37274011
|
2023-06-03 20:41:56 +00:00
|
|
|
* Copyright (C) 2020-2023 Kicad Developers, see AUTHORS.txt for contributors.
|
2020-01-03 15:09:26 +00:00
|
|
|
*
|
|
|
|
* 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 <widgets/split_button.h>
|
|
|
|
#include <wx/button.h>
|
|
|
|
#include <wx/dcclient.h>
|
|
|
|
#include <wx/dcmemory.h>
|
|
|
|
#include <wx/menu.h>
|
|
|
|
#include <wx/renderer.h>
|
2022-10-23 16:56:07 +00:00
|
|
|
#include <wx/settings.h>
|
2023-01-17 04:14:38 +00:00
|
|
|
#include <wx/version.h>
|
2022-10-23 16:56:07 +00:00
|
|
|
#include <kiplatform/ui.h>
|
2020-01-03 15:09:26 +00:00
|
|
|
|
|
|
|
SPLIT_BUTTON::SPLIT_BUTTON( wxWindow* aParent, wxWindowID aId, const wxString& aLabel,
|
2020-11-07 21:40:56 +00:00
|
|
|
const wxPoint& aPos, const wxSize& aSize ) :
|
2023-01-17 04:14:38 +00:00
|
|
|
wxPanel( aParent, aId, aPos, aSize, wxBORDER_NONE | wxTAB_TRAVERSAL, wxS( "DropDownButton" ) ),
|
2020-11-07 21:40:56 +00:00
|
|
|
m_label( aLabel )
|
2020-01-03 15:09:26 +00:00
|
|
|
{
|
2024-02-04 21:04:45 +00:00
|
|
|
m_arrowButtonWidth = FromDIP( 20 ); // just a fixed eyeballed constant width
|
|
|
|
m_widthPadding = FromDIP( 10 );
|
|
|
|
|
2020-01-03 15:09:26 +00:00
|
|
|
if( aSize == wxDefaultSize )
|
|
|
|
{
|
2023-08-22 18:32:25 +00:00
|
|
|
wxSize defaultSize = wxButton::GetDefaultSize( aParent );
|
2020-01-03 15:09:26 +00:00
|
|
|
wxSize textSize = GetTextExtent( m_label );
|
2022-12-16 19:10:02 +00:00
|
|
|
SetMinSize( wxSize( std::max( textSize.GetWidth(), defaultSize.GetWidth() + 1 ),
|
|
|
|
defaultSize.GetHeight() + 1 ) );
|
2020-01-03 15:09:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Bind( wxEVT_PAINT, &SPLIT_BUTTON::OnPaint, this );
|
|
|
|
Bind( wxEVT_LEFT_UP, &SPLIT_BUTTON::OnLeftButtonUp, this );
|
|
|
|
Bind( wxEVT_LEFT_DOWN, &SPLIT_BUTTON::OnLeftButtonDown, this );
|
|
|
|
Bind( wxEVT_KILL_FOCUS, &SPLIT_BUTTON::OnKillFocus, this );
|
|
|
|
Bind( wxEVT_LEAVE_WINDOW, &SPLIT_BUTTON::OnMouseLeave, this );
|
|
|
|
Bind( wxEVT_ENTER_WINDOW, &SPLIT_BUTTON::OnMouseEnter, this );
|
|
|
|
|
2022-10-23 16:56:07 +00:00
|
|
|
Bind( wxEVT_SYS_COLOUR_CHANGED, wxSysColourChangedEventHandler( SPLIT_BUTTON::onThemeChanged ),
|
|
|
|
this );
|
|
|
|
|
2020-01-03 15:09:26 +00:00
|
|
|
m_pMenu = new wxMenu();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SPLIT_BUTTON::~SPLIT_BUTTON()
|
|
|
|
{
|
|
|
|
delete m_pMenu;
|
|
|
|
m_pMenu = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-10-23 16:56:07 +00:00
|
|
|
void SPLIT_BUTTON::onThemeChanged( wxSysColourChangedEvent &aEvent )
|
|
|
|
{
|
|
|
|
Refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-01-03 15:09:26 +00:00
|
|
|
void SPLIT_BUTTON::SetMinSize( const wxSize& aSize )
|
|
|
|
{
|
|
|
|
m_unadjustedMinSize = aSize;
|
2020-11-07 21:40:56 +00:00
|
|
|
wxPanel::SetMinSize( wxSize( aSize.GetWidth() + m_arrowButtonWidth + m_widthPadding,
|
|
|
|
aSize.GetHeight() ) );
|
2020-01-03 15:09:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SPLIT_BUTTON::SetWidthPadding( int aPadding )
|
|
|
|
{
|
|
|
|
m_widthPadding = aPadding;
|
|
|
|
SetMinSize( m_unadjustedMinSize );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-03 16:41:40 +00:00
|
|
|
void SPLIT_BUTTON::SetBitmap( const wxBitmapBundle& aBmp )
|
2020-01-03 15:09:26 +00:00
|
|
|
{
|
|
|
|
m_bitmap = aBmp;
|
|
|
|
|
2023-06-09 12:58:52 +00:00
|
|
|
#ifndef __WXMSW__
|
2023-06-03 20:41:56 +00:00
|
|
|
SetMinSize( m_bitmap.GetDefaultSize() );
|
|
|
|
#else
|
2023-06-03 16:41:40 +00:00
|
|
|
SetMinSize( m_bitmap.GetPreferredBitmapSizeFor( this ) );
|
2023-06-03 20:41:56 +00:00
|
|
|
#endif
|
2020-01-03 15:09:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-28 11:40:06 +00:00
|
|
|
void SPLIT_BUTTON::SetLabel( const wxString& aLabel )
|
|
|
|
{
|
2022-12-27 23:55:09 +00:00
|
|
|
if( m_label != aLabel )
|
|
|
|
{
|
|
|
|
m_label = aLabel;
|
|
|
|
Refresh();
|
|
|
|
}
|
2022-08-28 11:40:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-01-03 15:09:26 +00:00
|
|
|
wxMenu* SPLIT_BUTTON::GetSplitButtonMenu()
|
|
|
|
{
|
|
|
|
return m_pMenu;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SPLIT_BUTTON::OnKillFocus( wxFocusEvent& aEvent )
|
|
|
|
{
|
2022-12-27 23:55:09 +00:00
|
|
|
if( m_stateButton != 0 || m_stateMenu != 0 )
|
|
|
|
{
|
|
|
|
m_stateButton = 0;
|
|
|
|
m_stateMenu = 0;
|
|
|
|
Refresh();
|
|
|
|
}
|
2020-01-03 15:09:26 +00:00
|
|
|
|
|
|
|
aEvent.Skip();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SPLIT_BUTTON::OnMouseLeave( wxMouseEvent& aEvent )
|
|
|
|
{
|
2022-12-27 23:55:09 +00:00
|
|
|
if( m_stateButton != 0 || m_stateMenu != 0 )
|
|
|
|
{
|
|
|
|
m_stateButton = 0;
|
|
|
|
m_stateMenu = 0;
|
|
|
|
Refresh();
|
|
|
|
}
|
2020-01-03 15:09:26 +00:00
|
|
|
|
|
|
|
aEvent.Skip();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SPLIT_BUTTON::OnMouseEnter( wxMouseEvent& aEvent )
|
|
|
|
{
|
2022-12-27 23:55:09 +00:00
|
|
|
if( m_stateButton != wxCONTROL_CURRENT || m_stateMenu != wxCONTROL_CURRENT )
|
|
|
|
{
|
|
|
|
m_stateButton = wxCONTROL_CURRENT;
|
|
|
|
m_stateMenu = wxCONTROL_CURRENT;
|
|
|
|
Refresh();
|
|
|
|
}
|
2020-01-03 15:09:26 +00:00
|
|
|
|
|
|
|
aEvent.Skip();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SPLIT_BUTTON::OnLeftButtonUp( wxMouseEvent& aEvent )
|
|
|
|
{
|
|
|
|
m_stateButton = 0;
|
|
|
|
m_stateMenu = 0;
|
|
|
|
|
|
|
|
Refresh();
|
|
|
|
|
|
|
|
int x = -1;
|
|
|
|
int y = -1;
|
|
|
|
aEvent.GetPosition( &x, &y );
|
|
|
|
|
|
|
|
if( x < ( GetSize().GetWidth() - m_arrowButtonWidth ) )
|
|
|
|
{
|
|
|
|
wxEvtHandler* pEventHandler = GetEventHandler();
|
|
|
|
wxASSERT( pEventHandler );
|
|
|
|
|
2020-11-07 21:40:56 +00:00
|
|
|
pEventHandler->CallAfter(
|
|
|
|
[=]()
|
|
|
|
{
|
2021-02-09 16:35:43 +00:00
|
|
|
wxCommandEvent evt( wxEVT_BUTTON, GetId() );
|
2020-11-07 21:40:56 +00:00
|
|
|
evt.SetEventObject( this );
|
|
|
|
GetEventHandler()->ProcessEvent( evt );
|
|
|
|
} );
|
2020-01-03 15:09:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m_bLButtonDown = false;
|
|
|
|
|
|
|
|
aEvent.Skip();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SPLIT_BUTTON::OnLeftButtonDown( wxMouseEvent& aEvent )
|
|
|
|
{
|
|
|
|
m_bLButtonDown = true;
|
|
|
|
|
|
|
|
int x = -1;
|
|
|
|
int y = -1;
|
|
|
|
aEvent.GetPosition( &x, &y );
|
|
|
|
|
|
|
|
if( x >= ( GetSize().GetWidth() - m_arrowButtonWidth ) )
|
|
|
|
{
|
|
|
|
m_stateButton = 0;
|
|
|
|
m_stateMenu = wxCONTROL_PRESSED;
|
|
|
|
Refresh();
|
|
|
|
|
|
|
|
wxSize size = GetSize();
|
|
|
|
wxPoint position;
|
|
|
|
position.x = 0;
|
|
|
|
position.y = size.GetHeight();
|
|
|
|
PopupMenu( m_pMenu, position );
|
|
|
|
|
|
|
|
m_stateMenu = 0;
|
|
|
|
Refresh();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_stateButton = wxCONTROL_PRESSED;
|
|
|
|
m_stateMenu = wxCONTROL_PRESSED;
|
|
|
|
Refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
aEvent.Skip();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SPLIT_BUTTON::OnPaint( wxPaintEvent& WXUNUSED( aEvent ) )
|
|
|
|
{
|
|
|
|
wxPaintDC dc( this );
|
|
|
|
wxSize size = GetSize();
|
|
|
|
const int width = size.GetWidth() - m_arrowButtonWidth;
|
|
|
|
|
2022-10-23 16:56:07 +00:00
|
|
|
#ifdef __WXMAC__
|
|
|
|
auto drawBackground =
|
|
|
|
[&]( wxRect aRect )
|
|
|
|
{
|
|
|
|
// wxWidgets doesn't have much support for dark mode on OSX; none of the
|
|
|
|
// system colours return the right values, nor does wxRendererNative draw
|
|
|
|
// the borders correctly. So we add some empirically chosen hacks here.
|
|
|
|
|
2022-12-14 12:57:40 +00:00
|
|
|
// NOTE: KEEP THESE HACKS IN SYNC WITH STD_BITMAP_BUTTON
|
|
|
|
|
2022-10-23 22:12:10 +00:00
|
|
|
wxColor fg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT );
|
2022-10-23 16:56:07 +00:00
|
|
|
wxColor bg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
|
|
|
|
|
2022-10-23 22:12:10 +00:00
|
|
|
aRect.width += 1;
|
|
|
|
aRect.height += 1;
|
|
|
|
|
2022-10-23 16:56:07 +00:00
|
|
|
if( KIPLATFORM::UI::IsDarkTheme() )
|
|
|
|
{
|
|
|
|
bg = bg.ChangeLightness( m_bIsEnable ? 130 : 120 );
|
2022-10-23 22:12:10 +00:00
|
|
|
dc.SetBrush( bg );
|
|
|
|
dc.SetPen( bg );
|
2022-10-23 16:56:07 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bg = bg.ChangeLightness( m_bIsEnable ? 200 : 160 );
|
2022-10-23 22:12:10 +00:00
|
|
|
dc.SetBrush( bg );
|
|
|
|
fg = fg.ChangeLightness( 180 );
|
|
|
|
dc.SetPen( fg );
|
2022-10-23 16:56:07 +00:00
|
|
|
}
|
|
|
|
|
2022-12-26 23:09:43 +00:00
|
|
|
dc.DrawRoundedRectangle( aRect, aRect.height / 4.0 );
|
2022-10-23 16:56:07 +00:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2020-01-03 15:09:26 +00:00
|
|
|
// Draw first part of button
|
|
|
|
wxRect r1;
|
|
|
|
r1.x = 0;
|
|
|
|
r1.y = 0;
|
2022-10-23 22:12:10 +00:00
|
|
|
r1.width = width;
|
2020-01-03 15:09:26 +00:00
|
|
|
r1.height = size.GetHeight();
|
|
|
|
|
2022-10-23 16:56:07 +00:00
|
|
|
#ifdef __WXMAC__
|
2022-10-23 22:12:10 +00:00
|
|
|
// wxRendereNative doesn't handle dark mode on OSX.
|
2022-10-23 16:56:07 +00:00
|
|
|
drawBackground( r1 );
|
2022-10-23 22:12:10 +00:00
|
|
|
#else
|
2022-12-26 23:09:43 +00:00
|
|
|
#ifdef _WXMSW_
|
|
|
|
r1.width += 2;
|
|
|
|
#endif
|
|
|
|
|
2022-10-23 22:12:10 +00:00
|
|
|
wxRendererNative::Get().DrawPushButton( this, dc, r1, m_stateButton );
|
2022-10-23 16:56:07 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
SetForegroundColour( m_bIsEnable ? wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT )
|
|
|
|
: wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
|
2020-01-03 15:09:26 +00:00
|
|
|
|
|
|
|
if( m_bitmap.IsOk() )
|
|
|
|
{
|
2023-06-15 03:23:36 +00:00
|
|
|
#ifndef __WXMSW__
|
2023-06-03 20:41:56 +00:00
|
|
|
wxSize bmpSize = m_bitmap.GetDefaultSize();
|
|
|
|
#else
|
|
|
|
wxSize bmpSize = m_bitmap.GetPreferredBitmapSizeFor( this );
|
|
|
|
#endif
|
2023-06-03 16:41:40 +00:00
|
|
|
wxBitmap bmp = m_bitmap.GetBitmapFor( this );
|
|
|
|
wxMemoryDC mdc( bmp );
|
2020-01-03 15:09:26 +00:00
|
|
|
|
2023-06-03 20:41:56 +00:00
|
|
|
r1.x = ( width - bmpSize.GetWidth() ) / 2;
|
2020-11-07 21:40:56 +00:00
|
|
|
|
2020-01-03 15:09:26 +00:00
|
|
|
if( r1.x < 0 )
|
|
|
|
r1.x = 0;
|
2020-11-07 21:40:56 +00:00
|
|
|
|
2023-06-03 20:41:56 +00:00
|
|
|
r1.y += ( size.GetHeight() - bmpSize.GetHeight() ) / 2;
|
2020-01-03 15:09:26 +00:00
|
|
|
|
2023-06-03 20:41:56 +00:00
|
|
|
dc.Blit( wxPoint( r1.x, r1.y ), bmpSize, &mdc, wxPoint( 0, 0 ), wxCOPY, true );
|
2020-01-03 15:09:26 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-10-23 22:12:10 +00:00
|
|
|
r1.y += ( ( size.GetHeight() - GetCharHeight() ) / 2 ) - 1;
|
2020-01-03 15:09:26 +00:00
|
|
|
dc.DrawLabel( m_label, r1, wxALIGN_CENTER_HORIZONTAL );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw second part of button
|
|
|
|
wxRect r2;
|
2022-10-23 22:12:10 +00:00
|
|
|
r2.x = width;
|
2020-01-03 15:09:26 +00:00
|
|
|
r2.y = 0;
|
|
|
|
r2.width = m_arrowButtonWidth;
|
|
|
|
r2.height = size.GetHeight();
|
|
|
|
|
2022-10-23 16:56:07 +00:00
|
|
|
#ifdef __WXMAC__
|
2022-10-23 22:12:10 +00:00
|
|
|
// wxRendereNative doesn't handle dark mode on OSX.
|
2022-10-23 16:56:07 +00:00
|
|
|
drawBackground( r2 );
|
2022-10-23 22:12:10 +00:00
|
|
|
#else
|
|
|
|
r2.x -= 2;
|
|
|
|
wxRendererNative::Get().DrawPushButton( this, dc, r2, m_stateMenu );
|
2022-10-23 16:56:07 +00:00
|
|
|
#endif
|
|
|
|
|
2020-01-03 15:09:26 +00:00
|
|
|
wxRendererNative::Get().DrawDropArrow( this, dc, r2, m_stateMenu );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool SPLIT_BUTTON::Enable( bool aEnable )
|
|
|
|
{
|
|
|
|
m_bIsEnable = aEnable;
|
|
|
|
wxPanel::Enable( m_bIsEnable );
|
|
|
|
|
2022-12-27 23:55:09 +00:00
|
|
|
if( m_bIsEnable
|
|
|
|
&& ( m_stateButton == wxCONTROL_DISABLED || m_stateMenu == wxCONTROL_DISABLED ) )
|
2020-01-03 15:09:26 +00:00
|
|
|
{
|
|
|
|
m_stateButton = 0;
|
|
|
|
m_stateMenu = 0;
|
2022-12-27 23:55:09 +00:00
|
|
|
Refresh();
|
2020-01-03 15:09:26 +00:00
|
|
|
}
|
2022-12-27 23:55:09 +00:00
|
|
|
|
|
|
|
if( !m_bIsEnable
|
|
|
|
&& ( m_stateButton != wxCONTROL_DISABLED || m_stateMenu != wxCONTROL_DISABLED ) )
|
2020-01-03 15:09:26 +00:00
|
|
|
{
|
|
|
|
m_stateButton = wxCONTROL_DISABLED;
|
|
|
|
m_stateMenu = wxCONTROL_DISABLED;
|
2022-12-27 23:55:09 +00:00
|
|
|
Refresh();
|
2020-01-03 15:09:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return aEnable;
|
2021-02-09 16:35:43 +00:00
|
|
|
}
|