KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
action_toolbar.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2019 CERN
5 * Copyright The KiCad Developers, see CHANGELOG.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <algorithm>
26#include <advanced_config.h>
27#include <bitmaps.h>
28#include <bitmap_store.h>
29#include <eda_draw_frame.h>
30#include <functional>
31#include <kiplatform/ui.h>
32#include <math/util.h>
33#include <memory>
34#include <pgm_base.h>
36#include <trace_helpers.h>
37#include <tool/action_toolbar.h>
38#include <tool/actions.h>
39#include <tool/tool_action.h>
40#include <tool/tool_event.h>
42#include <tool/tool_manager.h>
46
47#include <wx/log.h>
48#include <wx/popupwin.h>
49#include <wx/renderer.h>
50#include <wx/sizer.h>
51#include <wx/dcclient.h>
52#include <wx/settings.h>
53
54// Needed to handle adding the plugins to the toolbar
55// TODO (ISM): This should be better abstracted away from the toolbars
56#include <python_scripting.h>
58
59
60ACTION_GROUP::ACTION_GROUP( const std::string_view& aName )
61{
62 m_name = aName;
64}
65
66
67ACTION_GROUP::ACTION_GROUP( const std::string_view& aName,
68 const std::vector<const TOOL_ACTION*>& aActions )
69{
70 m_name = aName;
72
73 SetActions( aActions );
74}
75
76
77void ACTION_GROUP::SetActions( const std::vector<const TOOL_ACTION*>& aActions )
78{
79 wxASSERT_MSG( aActions.size() > 0, wxS( "Action groups must have at least one action" ) );
80
81 // The default action is just the first action in the vector
82 m_actions = aActions;
84}
85
87{
89}
90
91
93{
94 bool valid = std::any_of( m_actions.begin(), m_actions.end(),
95 [&]( const TOOL_ACTION* aAction ) -> bool
96 {
97 // For some reason, we can't compare the actions directly
98 return aAction->GetId() == aDefault.GetId();
99 } );
100
101 wxASSERT_MSG( valid, wxS( "Action must be present in a group to be the default" ) );
102
103 m_defaultAction = &aDefault;
104}
105
106
107#define PALETTE_BORDER 4 // The border around the palette buttons on all sides
108#define BUTTON_BORDER 1 // The border on the sides of the buttons that touch other buttons
109
110
111ACTION_TOOLBAR_PALETTE::ACTION_TOOLBAR_PALETTE( wxWindow* aParent, bool aVertical ) :
112 wxPopupTransientWindow( aParent, wxBORDER_NONE ),
113 m_group( nullptr ),
114 m_isVertical( aVertical ),
115 m_panel( nullptr ),
116 m_mainSizer( nullptr ),
117 m_buttonSizer( nullptr )
118{
119 m_panel = new wxPanel( this, wxID_ANY );
120 m_panel->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
121
122 // This sizer holds the buttons for the actions
123 m_buttonSizer = new wxBoxSizer( aVertical ? wxVERTICAL : wxHORIZONTAL );
124
125 // This sizer holds the other sizer, so that a consistent border is present on all sides
126 m_mainSizer = new wxBoxSizer( aVertical ? wxVERTICAL : wxHORIZONTAL );
127 m_mainSizer->Add( m_buttonSizer, wxSizerFlags().Border( wxALL, PALETTE_BORDER ) );
128
129 m_panel->SetSizer( m_mainSizer );
130
131 Connect( wxEVT_CHAR_HOOK, wxCharEventHandler( ACTION_TOOLBAR_PALETTE::onCharHook ),
132 nullptr, this );
133}
134
135
137{
139 wxBitmapBundle normalBmp = KiBitmapBundle( aAction.GetIcon(), size );
140
141 int bmpWidth = normalBmp.GetPreferredBitmapSizeFor( this ).GetWidth();
142 int padding = ( m_buttonSize.GetWidth() - bmpWidth ) / 2;
143 wxSize bmSize( size, size );
144 bmSize *= KIPLATFORM::UI::GetContentScaleFactor( m_parent );
145
146 BITMAP_BUTTON* button = new BITMAP_BUTTON( m_panel, aAction.GetUIId() );
147
148 button->SetIsToolbarButton();
149 button->SetBitmap( normalBmp );
150 button->SetDisabledBitmap( KiDisabledBitmapBundle( aAction.GetIcon() ) );
151 button->SetPadding( padding );
152 button->SetToolTip( aAction.GetButtonTooltip() );
153 button->AcceptDragInAsClick();
154 button->SetBitmapCentered();
155
156 m_buttons[aAction.GetUIId()] = button;
157
158 if( m_isVertical )
159 m_buttonSizer->Add( button, wxSizerFlags().Border( wxTOP | wxBOTTOM, BUTTON_BORDER ) );
160 else
161 m_buttonSizer->Add( button, wxSizerFlags().Border( wxLEFT | wxRIGHT, BUTTON_BORDER ) );
162
163 m_buttonSizer->Layout();
164}
165
166
167void ACTION_TOOLBAR_PALETTE::EnableAction( const TOOL_ACTION& aAction, bool aEnable )
168{
169 auto it = m_buttons.find( aAction.GetUIId() );
170
171 if( it != m_buttons.end() )
172 it->second->Enable( aEnable );
173}
174
175
176void ACTION_TOOLBAR_PALETTE::CheckAction( const TOOL_ACTION& aAction, bool aCheck )
177{
178 auto it = m_buttons.find( aAction.GetUIId() );
179
180 if( it != m_buttons.end() )
181 it->second->Check( aCheck );
182}
183
184
185void ACTION_TOOLBAR_PALETTE::Popup( wxWindow* aFocus )
186{
187 m_mainSizer->Fit( m_panel );
188 SetClientSize( m_panel->GetSize() );
189
190 wxPopupTransientWindow::Popup( aFocus );
191}
192
193
194void ACTION_TOOLBAR_PALETTE::onCharHook( wxKeyEvent& aEvent )
195{
196 // Allow the escape key to dismiss this popup
197 if( aEvent.GetKeyCode() == WXK_ESCAPE )
198 Dismiss();
199 else
200 aEvent.Skip();
201}
202
203
204ACTION_TOOLBAR::ACTION_TOOLBAR( EDA_BASE_FRAME* parent, wxWindowID id, const wxPoint& pos,
205 const wxSize& size, long style ) :
206 wxAuiToolBar( parent, id, pos, size, style ),
207 m_paletteTimer( nullptr ),
208 m_auiManager( nullptr ),
209 m_toolManager( parent->GetToolManager() ),
210 m_palette( nullptr )
211{
212 m_paletteTimer = new wxTimer( this );
213
214 SetArtProvider( new WX_AUI_TOOLBAR_ART );
215
216 Connect( wxEVT_COMMAND_TOOL_CLICKED, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onToolEvent ),
217 nullptr, this );
218 Connect( wxEVT_AUITOOLBAR_RIGHT_CLICK,
219 wxAuiToolBarEventHandler( ACTION_TOOLBAR::onToolRightClick ),
220 nullptr, this );
221 Connect( wxEVT_AUITOOLBAR_BEGIN_DRAG, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onItemDrag ),
222 nullptr, this );
223 Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr, this );
224 Connect( wxEVT_LEFT_UP, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr, this );
225 Connect( m_paletteTimer->GetId(), wxEVT_TIMER,
226 wxTimerEventHandler( ACTION_TOOLBAR::onTimerDone ), nullptr, this );
227
228 Bind( wxEVT_SYS_COLOUR_CHANGED,
229 wxSysColourChangedEventHandler( ACTION_TOOLBAR::onThemeChanged ), this );
230}
231
232
234{
235 Disconnect( wxEVT_COMMAND_TOOL_CLICKED, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onToolEvent ),
236 nullptr, this );
237 Disconnect( wxEVT_AUITOOLBAR_RIGHT_CLICK,
238 wxAuiToolBarEventHandler( ACTION_TOOLBAR::onToolRightClick ), nullptr, this );
239 Disconnect( wxEVT_AUITOOLBAR_BEGIN_DRAG, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onItemDrag ),
240 nullptr, this );
241 Disconnect( wxEVT_LEFT_DOWN, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr,
242 this );
243 Disconnect( wxEVT_LEFT_UP, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr, this );
244 Disconnect( m_paletteTimer->GetId(), wxEVT_TIMER,
245 wxTimerEventHandler( ACTION_TOOLBAR::onTimerDone ), nullptr, this );
246
247 Unbind( wxEVT_SYS_COLOUR_CHANGED,
248 wxSysColourChangedEventHandler( ACTION_TOOLBAR::onThemeChanged ), this );
249
250 delete m_paletteTimer;
251
252 // Clear all the maps keeping track of our items on the toolbar
253 m_toolMenus.clear();
254 m_actionGroups.clear();
255 m_toolCancellable.clear();
256 m_toolKinds.clear();
257 m_toolActions.clear();
258}
259
260
262{
263 wxASSERT( GetParent() );
264
265 // Remove existing tools
266 ClearToolbar();
267
268 std::vector<TOOLBAR_ITEM> items = aConfig.GetToolbarItems();
269
270 // Add all the items to the toolbar
271 for( auto& item : items )
272 {
273 switch( item.m_Type )
274 {
275 case TOOLBAR_ITEM_TYPE::SEPARATOR:
276 AddScaledSeparator( GetParent() );
277 break;
278
279 case TOOLBAR_ITEM_TYPE::SPACER:
280 AddSpacer( item.m_Size );
281 break;
282
283 case TOOLBAR_ITEM_TYPE::TB_GROUP:
284 {
285 // Add a group of items to the toolbar
286 std::vector<const TOOL_ACTION*> tools;
287
288 for( auto& groupItem : item.m_GroupItems )
289 {
290 switch( groupItem.m_Type )
291 {
292 case TOOLBAR_ITEM_TYPE::SEPARATOR:
293 case TOOLBAR_ITEM_TYPE::SPACER:
294 case TOOLBAR_ITEM_TYPE::TB_GROUP:
295 case TOOLBAR_ITEM_TYPE::CONTROL:
296 wxASSERT_MSG( false, "Unsupported group item type" );
297 continue;
298
299 case TOOLBAR_ITEM_TYPE::TOOL:
300 TOOL_ACTION* grpAction = m_toolManager->GetActionManager()->FindAction( groupItem.m_ActionName );
301
302 if( !grpAction )
303 {
304 wxASSERT_MSG( false, wxString::Format( "Unable to find group tool %s", groupItem.m_ActionName ) );
305 continue;
306 }
307
308 tools.push_back( grpAction );
309 }
310 }
311
312 AddGroup( std::make_unique<ACTION_GROUP>( item.m_GroupName.ToStdString(), tools ) );
313 break;
314 }
315
316 case TOOLBAR_ITEM_TYPE::CONTROL:
317 {
318 // Add a custom control to the toolbar
319 EDA_BASE_FRAME* frame = static_cast<EDA_BASE_FRAME*>( GetParent() );
320 ACTION_TOOLBAR_CONTROL_FACTORY* factory = frame->GetCustomToolbarControlFactory( item.m_ControlName );
321
322 if( !factory )
323 {
324 wxASSERT_MSG( false, wxString::Format( "Unable to find control factory for %s", item.m_ControlName ) );
325 continue;
326 }
327
328 // The factory functions are responsible for adding the controls to the toolbar themselves
329 (*factory)( this );
330 break;
331 }
332
333 case TOOLBAR_ITEM_TYPE::TOOL:
334 {
335 TOOL_ACTION* action = m_toolManager->GetActionManager()->FindAction( item.m_ActionName );
336
337 if( !action )
338 {
339 wxASSERT_MSG( false, wxString::Format( "Unable to find toolbar tool %s", item.m_ActionName ) );
340 continue;
341 }
342
343 Add( *action );
344 break;
345 }
346 }
347 }
348
349 // Apply the configuration
350 KiRealize();
351}
352
353
354void ACTION_TOOLBAR::Add( const TOOL_ACTION& aAction )
355{
356 wxASSERT_MSG( !aAction.CheckToolbarState( TOOLBAR_STATE::HIDDEN ),
357 wxString::Format( "Attempting to add hidden action %s to the toolbar", aAction.GetName() ) );
358
359 bool isToggleEntry = aAction.CheckToolbarState( TOOLBAR_STATE::TOGGLE );
360 bool isCancellable = aAction.CheckToolbarState( TOOLBAR_STATE::CANCEL );
361
362 Add( aAction, isToggleEntry, isCancellable );
363}
364
365
366void ACTION_TOOLBAR::Add( const TOOL_ACTION& aAction, bool aIsToggleEntry, bool aIsCancellable )
367{
368 wxASSERT( GetParent() );
369 wxASSERT_MSG( !( aIsCancellable && !aIsToggleEntry ),
370 wxS( "aIsCancellable requires aIsToggleEntry" ) );
371
372 int toolId = aAction.GetUIId();
373
374 AddTool( toolId, wxEmptyString,
375 KiBitmapBundle( aAction.GetIcon(),
376 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ),
377 KiDisabledBitmapBundle( aAction.GetIcon() ),
378 aIsToggleEntry ? wxITEM_CHECK : wxITEM_NORMAL,
379 aAction.GetButtonTooltip(), wxEmptyString, nullptr );
380
381 m_toolKinds[ toolId ] = aIsToggleEntry;
382 m_toolActions[ toolId ] = &aAction;
383 m_toolCancellable[ toolId ] = aIsCancellable;
384}
385
386
388{
389 int toolId = aAction.GetUIId();
390
391 AddTool( toolId, wxEmptyString,
392 KiBitmapBundle( aAction.GetIcon(),
393 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ),
394 KiDisabledBitmapBundle( aAction.GetIcon() ), wxITEM_NORMAL,
395 aAction.GetButtonTooltip(), wxEmptyString, nullptr );
396
397 m_toolKinds[ toolId ] = false;
398 m_toolActions[ toolId ] = &aAction;
399}
400
401
402void ACTION_TOOLBAR::AddScaledSeparator( wxWindow* aWindow )
403{
404 int scale = KiIconScale( aWindow );
405
406 if( scale > 4 )
407 AddSpacer( 16 * ( scale - 4 ) / 4 );
408
409 AddSeparator();
410
411 if( scale > 4 )
412 AddSpacer( 16 * ( scale - 4 ) / 4 );
413}
414
415
416void ACTION_TOOLBAR::Add( wxControl* aControl, const wxString& aLabel )
417{
418 wxASSERT( aControl );
419 m_controlIDs.push_back( aControl->GetId() );
420 AddControl( aControl, aLabel );
421}
422
423
425 std::unique_ptr<ACTION_MENU> aMenu )
426{
427 int toolId = aAction.GetUIId();
428
429 m_toolMenus[toolId] = std::move( aMenu );
430}
431
432
433void ACTION_TOOLBAR::AddGroup( std::unique_ptr<ACTION_GROUP> aGroup )
434{
435 int groupId = aGroup->GetUIId();
436 const TOOL_ACTION* defaultAction = aGroup->GetDefaultAction();
437
438 wxASSERT( GetParent() );
439 wxASSERT( defaultAction );
440
441 // Turn this into a toggle entry if any one of the actions is a toggle entry
442 bool isToggleEntry = false;
443
444 for( const auto& act : aGroup->GetActions() )
445 isToggleEntry |= act->CheckToolbarState( TOOLBAR_STATE::TOGGLE );
446
447
448 m_toolKinds[ groupId ] = isToggleEntry;
449 m_toolActions[ groupId ] = defaultAction;
450 m_actionGroups[ groupId ] = std::move( aGroup );
451
452 // Add the main toolbar item representing the group
453 AddTool( groupId, wxEmptyString,
454 KiBitmapBundle( defaultAction->GetIcon(),
455 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ),
456 KiDisabledBitmapBundle( defaultAction->GetIcon() ),
457 isToggleEntry ? wxITEM_CHECK : wxITEM_NORMAL,
458 wxEmptyString, wxEmptyString, nullptr );
459
460 // Select the default action
461 doSelectAction( m_actionGroups[ groupId ].get(), *defaultAction );
462}
463
464
466{
467 bool valid = std::any_of( aGroup->m_actions.begin(), aGroup->m_actions.end(),
468 [&]( const TOOL_ACTION* action2 ) -> bool
469 {
470 // For some reason, we can't compare the actions directly
471 return aAction.GetId() == action2->GetId();
472 } );
473
474 if( valid )
475 doSelectAction( aGroup, aAction );
476}
477
478
480{
481 wxASSERT( GetParent() );
482
483 int groupId = aGroup->GetUIId();
484
485 wxAuiToolBarItem* item = FindTool( groupId );
486
487 if( !item )
488 return;
489
490 // Update the item information
491 item->SetShortHelp( aAction.GetButtonTooltip() );
492 item->SetBitmap( KiBitmapBundle( aAction.GetIcon(),
493 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ) );
494 item->SetDisabledBitmap( KiDisabledBitmapBundle( aAction.GetIcon() ) );
495
496 // Register a new handler with the new UI conditions
497 if( m_toolManager )
498 {
500
502
503 // Register the new UI condition to control this entry
504 if( cond )
505 {
507 }
508 else
509 {
510 wxLogTrace( kicadTraceToolStack, wxString::Format( "No UI condition for action %s",
511 aAction.GetName() ) );
512 }
513 }
514
515 // Update the currently selected action
516 m_toolActions[ groupId ] = &aAction;
517
518 Refresh();
519}
520
521
523{
524 for( int id : m_controlIDs )
525 UpdateControlWidth( id );
526}
527
528
530{
531 wxAuiToolBarItem* item = FindTool( aID );
532 wxASSERT_MSG( item, wxString::Format( "No toolbar item found for ID %d", aID ) );
533
534 // The control on the toolbar is stored inside the window field of the item
535 wxControl* control = dynamic_cast<wxControl*>( item->GetWindow() );
536 wxASSERT_MSG( control,
537 wxString::Format( "No control located in toolbar item with ID %d", aID ) );
538
539 // Update the size the item has stored using the best size of the control
540 control->InvalidateBestSize();
541 wxSize bestSize = control->GetBestSize();
542 item->SetMinSize( bestSize );
543
544 // Update the sizer item sizes
545 // This is a bit convoluted because there are actually 2 sizers that need to be updated:
546 // 1. The main sizer that is used for the entire toolbar (this sizer item can be found in the
547 // toolbar item)
548 if( wxSizerItem* szrItem = item->GetSizerItem() )
549 szrItem->SetMinSize( bestSize );
550
551 // 2. The controls have a second sizer that allows for padding above/below the control with
552 // stretch space, so we also need to update the sizer item for the control in that sizer with
553 // the new size. We let wx do the search for us, since SetItemMinSize is recursive and will
554 // locate the control on that sizer.
555 if( m_sizer )
556 {
557 m_sizer->SetItemMinSize( control, bestSize );
558
559 // Now actually update the toolbar with the new sizes
560 m_sizer->Layout();
561 }
562}
563
564
566{
567 // Clear all the maps keeping track of our items on the toolbar
568 m_toolMenus.clear();
569 m_actionGroups.clear();
570 m_toolCancellable.clear();
571 m_toolKinds.clear();
572 m_toolActions.clear();
573
574 // Remove the actual tools from the toolbar
575 Clear();
576}
577
578
579void ACTION_TOOLBAR::SetToolBitmap( const TOOL_ACTION& aAction, const wxBitmap& aBitmap )
580{
581 int toolId = aAction.GetUIId();
582 wxAuiToolBar::SetToolBitmap( toolId, aBitmap );
583
584 // Set the disabled bitmap: we use the disabled bitmap version of aBitmap.
585 wxAuiToolBarItem* tb_item = wxAuiToolBar::FindTool( toolId );
586
587 if( tb_item )
588 {
589 tb_item->SetDisabledBitmap(
590 aBitmap.ConvertToDisabled( KIPLATFORM::UI::IsDarkTheme() ? 70 : 255 ) );
591 }
592}
593
594
595void ACTION_TOOLBAR::Toggle( const TOOL_ACTION& aAction, bool aState )
596{
597 int toolId = aAction.GetUIId();
598
599 if( m_toolKinds[ toolId ] )
600 ToggleTool( toolId, aState );
601 else
602 EnableTool( toolId, aState );
603}
604
605
606void ACTION_TOOLBAR::Toggle( const TOOL_ACTION& aAction, bool aEnabled, bool aChecked )
607{
608 int toolId = aAction.GetUIId();
609
610 EnableTool( toolId, aEnabled );
611 ToggleTool( toolId, aEnabled && aChecked );
612}
613
614
615void ACTION_TOOLBAR::onToolEvent( wxAuiToolBarEvent& aEvent )
616{
617 int id = aEvent.GetId();
618 wxEventType type = aEvent.GetEventType();
619 OPT_TOOL_EVENT evt;
620
621 bool handled = false;
622
623 if( m_toolManager && type == wxEVT_COMMAND_TOOL_CLICKED && id >= TOOL_ACTION::GetBaseUIId() )
624 {
625 const auto actionIt = m_toolActions.find( id );
626
627 // The toolbar item is toggled before the event is sent, so we check for it not being
628 // toggled to see if it was toggled originally
629 if( m_toolCancellable[id] && !GetToolToggled( id ) )
630 {
631 // Send a cancel event
633 handled = true;
634 }
635 else if( actionIt != m_toolActions.end() )
636 {
637 // Dispatch a tool event
638 evt = actionIt->second->MakeEvent();
639 evt->SetHasPosition( false );
642 handled = true;
643 }
644 }
645
646 // Skip the event if we don't handle it
647 if( !handled )
648 aEvent.Skip();
649}
650
651
652void ACTION_TOOLBAR::onToolRightClick( wxAuiToolBarEvent& aEvent )
653{
654 int toolId = aEvent.GetToolId();
655
656 // This means the event was not on a button
657 if( toolId == -1 )
658 return;
659
660 // Ensure that the ID used maps to a proper tool ID.
661 // If right-clicked on a group item, this is needed to get the ID of the currently selected
662 // action, since the event's ID is that of the group.
663 const auto actionIt = m_toolActions.find( toolId );
664
665 if( actionIt != m_toolActions.end() )
666 toolId = actionIt->second->GetUIId();
667
668 // Find the menu for the action
669 const auto menuIt = m_toolMenus.find( toolId );
670
671 if( menuIt == m_toolMenus.end() )
672 return;
673
674 // Update and show the menu
675 std::unique_ptr<ACTION_MENU>& owningMenu = menuIt->second;
676
677 // Get the actual menu pointer to show it
678 ACTION_MENU* menu = owningMenu.get();
679 SELECTION dummySel;
680
681 if( CONDITIONAL_MENU* condMenu = dynamic_cast<CONDITIONAL_MENU*>( menu ) )
682 condMenu->Evaluate( dummySel );
683
684 menu->UpdateAll();
685 PopupMenu( menu );
686
687 // Remove hovered item when the menu closes, otherwise it remains hovered even if the
688 // mouse is not on the toolbar
689 SetHoverItem( nullptr );
690}
691
692
693// The time (in milliseconds) between pressing the left mouse button and opening the palette
694#define PALETTE_OPEN_DELAY 500
695
696
697void ACTION_TOOLBAR::onMouseClick( wxMouseEvent& aEvent )
698{
699 wxAuiToolBarItem* item = FindToolByPosition( aEvent.GetX(), aEvent.GetY() );
700
701 if( item )
702 {
703 // Ensure there is no active palette
704 if( m_palette )
705 {
706 m_palette->Hide();
707 m_palette->Destroy();
708 m_palette = nullptr;
709 }
710
711 // Start the popup conditions if it is a left mouse click and the tool clicked is a group
712 if( aEvent.LeftDown() && ( m_actionGroups.find( item->GetId() ) != m_actionGroups.end() ) )
714
715 // Clear the popup conditions if it is a left up, because that implies a click happened
716 if( aEvent.LeftUp() )
717 m_paletteTimer->Stop();
718 }
719
720 // Skip the event so wx can continue processing the mouse event
721 aEvent.Skip();
722}
723
724
725void ACTION_TOOLBAR::onItemDrag( wxAuiToolBarEvent& aEvent )
726{
727 int toolId = aEvent.GetToolId();
728
729 if( m_actionGroups.find( toolId ) != m_actionGroups.end() )
730 {
731 wxAuiToolBarItem* item = FindTool( toolId );
732
733 // Use call after because opening the palette from a mouse handler
734 // creates a weird mouse state that causes problems on OSX.
735 CallAfter( &ACTION_TOOLBAR::popupPalette, item );
736
737 // Don't skip this event since we are handling it
738 return;
739 }
740
741 // Skip since we don't care about it
742 aEvent.Skip();
743}
744
745
746void ACTION_TOOLBAR::onTimerDone( wxTimerEvent& aEvent )
747{
748 // We need to search for the tool using the client coordinates
749 wxPoint mousePos = ScreenToClient( KIPLATFORM::UI::GetMousePosition() );
750
751 wxAuiToolBarItem* item = FindToolByPosition( mousePos.x, mousePos.y );
752
753 if( item )
754 popupPalette( item );
755}
756
757
758void ACTION_TOOLBAR::onPaletteEvent( wxCommandEvent& aEvent )
759{
760 if( !m_palette )
761 return;
762
763 OPT_TOOL_EVENT evt;
765
766 // Find the action corresponding to the button press
767 auto actionIt = std::find_if( group->GetActions().begin(), group->GetActions().end(),
768 [&]( const TOOL_ACTION* aAction )
769 {
770 return aAction->GetUIId() == aEvent.GetId();
771 } );
772
773 if( actionIt != group->GetActions().end() )
774 {
775 const TOOL_ACTION* action = *actionIt;
776
777 // Dispatch a tool event
778 evt = action->MakeEvent();
779 evt->SetHasPosition( false );
782
783 // Update the main toolbar item with the selected action
784 doSelectAction( group, *action );
785 }
786
787 // Hide the palette
788 m_palette->Hide();
789 m_palette->Destroy();
790 m_palette = nullptr;
791}
792
793
794void ACTION_TOOLBAR::popupPalette( wxAuiToolBarItem* aItem )
795{
796 // Clear all popup conditions
797 m_paletteTimer->Stop();
798
799 wxWindow* toolParent = dynamic_cast<wxWindow*>( m_toolManager->GetToolHolder() );
800
801 wxASSERT( GetParent() );
802 wxASSERT( m_auiManager );
803 wxASSERT( toolParent );
804
805 // Ensure the item we are using for the palette has a group associated with it.
806 const auto it = m_actionGroups.find( aItem->GetId() );
807
808 if( it == m_actionGroups.end() )
809 return;
810
811 ACTION_GROUP* group = it->second.get();
812
813 wxAuiPaneInfo& pane = m_auiManager->GetPane( this );
814
815 // We use the size of the toolbar items for our palette buttons
816 wxRect toolRect = GetToolRect( aItem->GetId() );
817
818 // The position for the palette window must be in screen coordinates
819 wxPoint pos( ClientToScreen( toolRect.GetPosition() ) );
820
821 // True for vertical buttons, false for horizontal
822 bool dir = true;
823 size_t numActions = group->m_actions.size();
824
825 // The size of the palette in the long dimension
826 int paletteLongDim = ( 2 * PALETTE_BORDER ) // The border on all sides of the buttons
827 + ( BUTTON_BORDER ) // The border on the start of the buttons
828 + ( numActions * BUTTON_BORDER ) // The other button borders
829 + ( numActions * toolRect.GetHeight() ); // The size of the buttons
830
831 // Determine the position of the top left corner of the palette window
832 switch( pane.dock_direction )
833 {
834 case wxAUI_DOCK_TOP:
835 // Top toolbars need to shift the palette window down by the toolbar padding
836 dir = true; // Buttons are vertical in the palette
837 pos = ClientToScreen( toolRect.GetBottomLeft() );
838 pos += wxPoint( -PALETTE_BORDER, // Shift left to align the button edges
839 m_bottomPadding ); // Shift down to move away from the toolbar
840 break;
841
842 case wxAUI_DOCK_BOTTOM:
843 // Bottom toolbars need to shift the palette window up by its height (all buttons +
844 // border + toolbar padding)
845 dir = true; // Buttons are vertical in the palette
846 pos = ClientToScreen( toolRect.GetTopLeft() );
847 pos += wxPoint( -PALETTE_BORDER, // Shift left to align the button
848 // Shift up by the entire length of the palette.
849 -( paletteLongDim + m_topPadding ) );
850 break;
851
852 case wxAUI_DOCK_LEFT:
853 // Left toolbars need to shift the palette window up by the toolbar padding
854 dir = false; // Buttons are horizontal in the palette
855 pos = ClientToScreen( toolRect.GetTopRight() );
856 pos += wxPoint( m_rightPadding, // Shift right to move away from the toolbar
857 -( PALETTE_BORDER ) ); // Shift up to align the button tops
858 break;
859
860 case wxAUI_DOCK_RIGHT:
861 // Right toolbars need to shift the palette window left by its width (all buttons +
862 // border + toolbar padding)
863 dir = false; // Buttons are horizontal in the palette
864 pos = ClientToScreen( toolRect.GetTopLeft() );
865
866 // Shift left by the entire length of the palette.
867 pos += wxPoint( -( paletteLongDim + m_leftPadding ),
868 -( PALETTE_BORDER ) ); // Shift up to align the button
869 break;
870 }
871
872 m_palette = new ACTION_TOOLBAR_PALETTE( GetParent(), dir );
873
874 // We handle the button events in the toolbar class, so connect the right handler
876 m_palette->SetButtonSize( toolRect );
877 m_palette->Connect( wxEVT_BUTTON, wxCommandEventHandler( ACTION_TOOLBAR::onPaletteEvent ),
878 nullptr, this );
879
880
881 // Add the actions in the group to the palette and update their enabled state
882 // We purposely don't check items in the palette
883 for( const TOOL_ACTION* action : group->m_actions )
884 {
885 wxUpdateUIEvent evt( action->GetUIId() );
886
887 toolParent->ProcessWindowEvent( evt );
888
889 m_palette->AddAction( *action );
890
891 if( evt.GetSetEnabled() )
892 m_palette->EnableAction( *action, evt.GetEnabled() );
893 }
894
895 // Release the mouse to ensure the first click will be recognized in the palette
896 ReleaseMouse();
897
898 m_palette->SetPosition( pos );
899 m_palette->Popup();
900
901 // Clear the mouse state on the toolbar because otherwise wxWidgets gets confused
902 // and won't properly display any highlighted items after the palette is closed.
903 // (This is the equivalent of calling the DoResetMouseState() private function)
904 RefreshOverflowState();
905 SetHoverItem( nullptr );
906 SetPressedItem( nullptr );
907
908 m_dragging = false;
909 m_tipItem = nullptr;
910 m_actionPos = wxPoint( -1, -1 );
911 m_actionItem = nullptr;
912}
913
914
915void ACTION_TOOLBAR::OnCustomRender(wxDC& aDc, const wxAuiToolBarItem& aItem, const wxRect& aRect )
916{
917 auto it = m_actionGroups.find( aItem.GetId() );
918
919 if( it == m_actionGroups.end() )
920 return;
921
922 // Choose the color to draw the triangle
923 wxColour clr;
924
925 if( aItem.GetState() & wxAUI_BUTTON_STATE_DISABLED )
926 clr = wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT );
927 else
928 clr = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT );
929
930 // Must set both the pen (for the outline) and the brush (for the polygon fill)
931 aDc.SetPen( wxPen( clr ) );
932 aDc.SetBrush( wxBrush( clr ) );
933
934 // Make the side length of the triangle approximately 1/5th of the bitmap
935 int sideLength = KiROUND( aRect.height / 5.0 );
936
937 // This will create a triangle with its point at the bottom right corner,
938 // and its other two corners along the right and bottom sides
939 wxPoint btmRight = aRect.GetBottomRight();
940 wxPoint topCorner( btmRight.x, btmRight.y - sideLength );
941 wxPoint btmCorner( btmRight.x - sideLength, btmRight.y );
942
943 wxPointList points;
944 points.Append( &btmRight );
945 points.Append( &topCorner );
946 points.Append( &btmCorner );
947
948 aDc.DrawPolygon( &points );
949}
950
951
953{
954#if wxCHECK_VERSION( 3, 3, 0 )
955 return Realize();
956#else
957 wxClientDC dc( this );
958
959 if( !dc.IsOk() )
960 return false;
961
962 // calculate hint sizes for both horizontal and vertical
963 // in the order that leaves toolbar in correct final state
964
965 // however, skip calculating alternate orientations if we don't need them due to window style
966 bool retval = true;
967
968 if( m_orientation == wxHORIZONTAL )
969 {
970 if( !( GetWindowStyle() & wxAUI_TB_HORIZONTAL ) )
971 {
972 m_vertHintSize = GetSize();
973 retval = RealizeHelper( dc, false );
974 }
975
976 if( retval && RealizeHelper( dc, true ) )
977 {
978 m_horzHintSize = GetSize();
979 }
980 else
981 {
982 retval = false;
983 }
984 }
985 else
986 {
987 if( !( GetWindowStyle() & wxAUI_TB_VERTICAL ) )
988 {
989 m_horzHintSize = GetSize();
990 retval = RealizeHelper( dc, true );
991 }
992
993 if( retval && RealizeHelper( dc, false ) )
994 {
995 m_vertHintSize = GetSize();
996 }
997 else
998 {
999 retval = false;
1000 }
1001 }
1002
1003 Refresh( false );
1004 return retval;
1005#endif
1006}
1007
1008
1009void ACTION_TOOLBAR::onThemeChanged( wxSysColourChangedEvent &aEvent )
1010{
1013
1014 aEvent.Skip();
1015}
1016
1017
1019{
1020 for( const std::pair<int, const TOOL_ACTION*> pair : m_toolActions )
1021 {
1022 wxAuiToolBarItem* tool = FindTool( pair.first );
1023
1024 tool->SetBitmap(
1025 KiBitmapBundle( pair.second->GetIcon(),
1026 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ) );
1027 tool->SetDisabledBitmap( KiDisabledBitmapBundle( pair.second->GetIcon() ) );
1028 }
1029
1030 Refresh();
1031}
1032
1033/*
1034 * Common controls for the toolbar
1035 */
1036ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::gridSelect( "control.GridSelector", _( "Grid Selector" ),
1037 _( "Grid Selection box" ) );
1038
1039ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::zoomSelect( "control.ZoomSelector", _( "Zoom Selector" ),
1040 _( "Zoom Selection box" ) );
1041
1042ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::ipcScripting( "control.IPCPlugin", _( "IPC/Scripting plugins" ),
1043 _( "Region to hold the IPC/Scripting action buttons" ) );
1044
1045ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::layerSelector( "control.LayerSelector", _( "Layer selector" ),
1046 _( "Control to select the layer" ) );
1047
1048ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::unitSelector( "control.UnitSelector", _( "Symbol unit selector" ),
1049 _( "Displays the current unit" ) );
#define PALETTE_BORDER
#define PALETTE_OPEN_DELAY
#define BUTTON_BORDER
std::function< void(ACTION_TOOLBAR *)> ACTION_TOOLBAR_CONTROL_FACTORY
Type for the function signature that is used to add custom controls to the toolbar.
int KiIconScale(wxWindow *aWindow)
Return the automatic scale factor that would be used for a given window by KiScaledBitmap and KiScale...
Definition: bitmap.cpp:122
BITMAP_STORE * GetBitmapStore()
Definition: bitmap.cpp:92
wxBitmapBundle KiDisabledBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:116
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition: bitmap.cpp:110
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
A group of actions that will be displayed together on a toolbar palette.
void SetDefaultAction(const TOOL_ACTION &aDefault)
Set the default action to use when first creating the toolbar palette icon.
ACTION_GROUP(const std::string_view &aName)
std::vector< const TOOL_ACTION * > m_actions
void SetActions(const std::vector< const TOOL_ACTION * > &aActions)
Set the actions contained in this group.
int GetUIId() const
Get the ID used in the UI to reference this group.
int m_id
< The action ID for this action group
const TOOL_ACTION * m_defaultAction
The actions that compose the group.
std::string m_name
The default action to display on the toolbar item.
const ACTION_CONDITIONS * GetCondition(const TOOL_ACTION &aAction) const
Get the conditions to use for a specific tool action.
TOOL_ACTION * FindAction(const std::string &aActionName) const
Find an action with a given name (if there is one available).
static int MakeActionId(const std::string &aActionName)
Generate an unique ID from for an action with given name.
Define the structure of a menu based on ACTIONs.
Definition: action_menu.h:49
void UpdateAll()
Run update handlers for the menu and its submenus.
static ACTION_TOOLBAR_CONTROL gridSelect
static ACTION_TOOLBAR_CONTROL layerSelector
static ACTION_TOOLBAR_CONTROL zoomSelect
static ACTION_TOOLBAR_CONTROL unitSelector
static ACTION_TOOLBAR_CONTROL ipcScripting
Class to hold basic information about controls that can be added to the toolbars.
A popup window that contains a row of toolbar-like buttons for the user to choose from.
void CheckAction(const TOOL_ACTION &aAction, bool aCheck=true)
Check/Toggle the button for an action on the palette.
void onCharHook(wxKeyEvent &aEvent)
wxBoxSizer * m_buttonSizer
The buttons that act as the toolbar on the palette.
void AddAction(const TOOL_ACTION &aAction)
Add an action to the palette.
ACTION_TOOLBAR_PALETTE(wxWindow *aParent, bool aVertical)
Create the palette.
void SetButtonSize(wxRect &aSize)
Set the size all the buttons on this palette should be.
void SetGroup(ACTION_GROUP *aGroup)
Set the action group that this palette contains the actions for.
wxRect m_buttonSize
True if the palette uses vertical buttons, false for horizontal buttons.
void EnableAction(const TOOL_ACTION &aAction, bool aEnable=true)
Enable the button for an action on the palette.
std::map< int, BITMAP_BUTTON * > m_buttons
ACTION_GROUP * GetGroup()
void Popup(wxWindow *aFocus=nullptr) override
Popup this window.
void RefreshBitmaps()
Reload all the bitmaps for the tools (e.g.
void OnCustomRender(wxDC &aDc, const wxAuiToolBarItem &aItem, const wxRect &aRect) override
void onTimerDone(wxTimerEvent &aEvent)
void doSelectAction(ACTION_GROUP *aGroup, const TOOL_ACTION &aAction)
Update a group toolbar item to look like a specific action.
void onMouseClick(wxMouseEvent &aEvent)
Handler for when a drag event occurs on an item.
void onToolRightClick(wxAuiToolBarEvent &aEvent)
Handle the button select inside the palette.
void AddButton(const TOOL_ACTION &aAction)
Add a large button such as used in the KiCad Manager Frame's launch bar.
wxAuiManager * m_auiManager
void UpdateControlWidth(int aID)
Update the toolbar item width of a control using its best size.
ACTION_TOOLBAR(EDA_BASE_FRAME *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxAUI_TB_DEFAULT_STYLE)
void Toggle(const TOOL_ACTION &aAction, bool aState)
Apply the default toggle action.
void SelectAction(ACTION_GROUP *aGroup, const TOOL_ACTION &aAction)
Select an action inside a group.
void Add(const TOOL_ACTION &aAction)
Add a TOOL_ACTION-based button to the toolbar.
void onPaletteEvent(wxCommandEvent &aEvent)
Handle the palette timer triggering.
wxTimer * m_paletteTimer
std::map< int, std::unique_ptr< ACTION_MENU > > m_toolMenus
void onToolEvent(wxAuiToolBarEvent &aEvent)
Handle a right-click on a menu item.
void onItemDrag(wxAuiToolBarEvent &aEvent)
The default tool event handler.
std::map< int, bool > m_toolKinds
std::map< int, std::unique_ptr< ACTION_GROUP > > m_actionGroups
void AddGroup(std::unique_ptr< ACTION_GROUP > aGroup)
Add a set of actions to a toolbar as a group.
void AddToolContextMenu(const TOOL_ACTION &aAction, std::unique_ptr< ACTION_MENU > aMenu)
Add a context menu to a specific tool item on the toolbar.
std::map< int, bool > m_toolCancellable
void AddScaledSeparator(wxWindow *aWindow)
Add a separator that introduces space on either side to not squash the tools when scaled.
bool KiRealize()
Use this over Realize() to avoid a rendering glitch with fixed orientation toolbars.
void popupPalette(wxAuiToolBarItem *aItem)
Popup the ACTION_TOOLBAR_PALETTE associated with the ACTION_GROUP of the given toolbar item.
virtual ~ACTION_TOOLBAR()
ACTION_TOOLBAR_PALETTE * m_palette
std::vector< int > m_controlIDs
IDs for all the control items in this toolbar.
void onThemeChanged(wxSysColourChangedEvent &aEvent)
Render the triangle in the lower-right corner that represents that an action palette is available for...
std::map< int, const TOOL_ACTION * > m_toolActions
void ClearToolbar()
Clear the toolbar and remove all associated menus.
void SetToolBitmap(const TOOL_ACTION &aAction, const wxBitmap &aBitmap)
Updates the bitmap of a particular tool.
void UpdateControlWidths()
Update the width of all wxControl tools on thsi toolbar.
void ApplyConfiguration(const TOOLBAR_CONFIGURATION &aConfig)
Replace the contents of this toolbar with the configuration given in aConfig.
TOOL_MANAGER * m_toolManager
A bitmap button widget that behaves like an AUI toolbar item's button when it is drawn.
Definition: bitmap_button.h:42
void AcceptDragInAsClick(bool aAcceptDragIn=true)
Accept mouse-up as click even if mouse-down happened outside of the control.
void SetDisabledBitmap(const wxBitmapBundle &aBmp)
Set the bitmap shown when the button is disabled.
void SetBitmapCentered(bool aCentered=true)
void SetIsToolbarButton(bool aIsToolbar=true)
void SetBitmap(const wxBitmapBundle &aBmp)
Set the bitmap shown when the button is enabled.
void SetPadding(int aPadding)
Set the amount of padding present on each side of the bitmap.
void ThemeChanged()
Notifies the store that the icon theme has been changed by the user, so caches must be invalidated.
APPEARANCE m_Appearance
The base frame for deriving all KiCad main window classes.
ACTION_TOOLBAR_CONTROL_FACTORY * GetCustomToolbarControlFactory(const std::string &aName)
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition: pgm_base.cpp:687
std::vector< TOOLBAR_ITEM > GetToolbarItems() const
virtual void RegisterUIUpdateHandler(const TOOL_ACTION &aAction, const ACTION_CONDITIONS &aConditions)
Register an action's update conditions with the UI layer to allow the UI to appropriately display the...
virtual void UnregisterUIUpdateHandler(const TOOL_ACTION &aAction)
Unregister a UI handler for an action that was registered using RegisterUIUpdateHandler.
virtual void RefreshCanvas()
Definition: tools_holder.h:166
Represent a single user action.
Definition: tool_action.h:304
static int GetBaseUIId()
Get the base value used to offset the user interface IDs for the actions.
Definition: tool_action.h:379
BITMAPS GetIcon() const
Return an icon associated with the action.
Definition: tool_action.h:459
const std::string & GetName() const
Return name of the action.
Definition: tool_action.h:337
bool CheckToolbarState(TOOLBAR_STATE aState) const
Check if a specific toolbar state is required for this action.
Definition: tool_action.h:473
TOOL_EVENT MakeEvent() const
Return the event associated with the action (i.e.
wxString GetButtonTooltip() const
int GetUIId() const
Get the unique ID for this action in the user interface system.
Definition: tool_action.h:374
void SetHasPosition(bool aHasPosition)
Definition: tool_event.h:258
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
void CancelTool()
Send a cancel event to the tool currently at the top of the tool stack.
TOOLS_HOLDER * GetToolHolder() const
Definition: tool_manager.h:406
ACTION_MANAGER * GetActionManager() const
Definition: tool_manager.h:306
#define _(s)
const wxChar *const kicadTraceToolStack
Flag to enable tracing of the tool handling stack.
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition: wxgtk/ui.cpp:677
double GetContentScaleFactor(const wxWindow *aWindow)
Tries to determine the content scaling factor currently in use for the window.
Definition: wxgtk/ui.cpp:245
bool IsDarkTheme()
Determine if the desktop interface is currently using a dark theme or a light theme.
Definition: wxgtk/ui.cpp:48
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1071
see class PGM_BASE
const int scale
Functors that can be used to figure out how the action controls should be displayed in the UI and if ...
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition: tool_event.h:633
wxLogTrace helper definitions.